目录

  • 顺序表
    • 一、顺序表的定义
    • 二、顺序表上基本操作的具体实现
      • 1.`InitList_Sq(*L)`:初始化表。构造一个空的顺序表。
        • 分类一:主函数里声明的是一个指向顺序表的指针
        • 分类二:主函数里声明的是一个顺序表
      • 2. `Length(L)`:求表长。
      • 3.`ListInsert_Sq(*L,i,e)`:插入操作。在顺序表L中的第i个位置上插入指定元素e。
      • 4.`ListDelete_Sq(*L,i,*e)`:删除操作。删除表中L中第i个位置的元素,并用e返回删除的元素。
      • 5.`LocateElem(L,e)`:按值查找操作。在表L中查找具有给定关键字值的元素。
      • 6.`GetElem(L,i,*e)`:按位查找操作。获取表L中第i个位置的元素的值。
      • 7.`PrintList(L)`:输出操作。按前后顺序输出线性表L的所有元素值。
      • 8.`Empty(L)`:判空操作。
      • 9.`DestroyList(*L)`:销毁操作。销毁顺序表,并释放顺序表L所占用的内存空间。
    • 三、完整代码实现
    • 四、分析操作的复杂度
    • 五、总结

顺序表

上篇文章中我们已经介绍了线性表。
这篇文章我们将介绍线性表的顺序存储(具体实现)----顺序表

一、顺序表的定义

仍旧使用该例子,我们要如何存储该表呢?

id name description
1 史强 最强辅助
2 章北海 我不需要钢印,我是自己思想的主人
3 罗辑 人类不感谢罗辑
4 维德 失去人性,失去很多。失去兽性,失去一切

步骤一:声明数据元素类型

首先我们使用结构体声明数据元素(对应表中的一行数据)的类型
关于数据元素的描述可以查看上篇文章。

typedef struct{int id;//对应表中的idchar name[100];//对应表中的姓名char description[200];//对应表中的描述
}ElemType;//此处的ElemType是个类型名

步骤二:顺序表的静态定义
步骤一中已经定义了数据元素,那么n个数据元素如何存储?

我们采用数组来存储所有的数据元素
注意:用数组的话我们在定义时需要声明数组的大小,这里使用变量MaxSize

那又如何确定已经存储了多少数据元素呢?(也就是表有多少行?)

我们需要用一个变量length来存储当前的长度

#define MaxSize 10  //定义顺序表的最大长度,在声明数组时使用
typedef struct{ElemType data[MaxSize];//顺序表的数据元素int length;//顺序表的当前长度
}Sqlist;//顺序表的类型定义

步骤三:顺序表的动态定义
上面的定义有什么问题呢?

采用静态分配时,由于数组的大小和空间事先已经固定,一旦空间占满,再加入新的数据就会产生溢出,进而导致程序崩溃

如何改进?

采用动态分配(关于动态分配的内容可以查看博文)

#define InitSize 10;//表的初始长度
#define ListIncrement 2;//扩容时的分配增量
typedef struct{ElemType *data;//数组的基地址,指示动态分配的数组的指针int length;//当前长度int MaxSize;//当前表的最大存储长度
}Sqlist;//顺序表的类型定义

二、顺序表上基本操作的具体实现

  • InitList_Sq(*L):初始化表。构造一个空的顺序表。
  • Length(L):求表长。
  • ListInsert(*L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。
  • ListDelete(*L,i,*e):删除操作。删除表中L中第i个位置的元素,并用e返回删除的元素。
  • LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。
  • GetElem(L,i,*e):按位查找操作。获取表L中第i个位置的元素的值。
  • PrintList(L):输出操作。按前后顺序输出顺序表L的所有元素值。
  • Empty(L):判空操作。
  • DestroyList(*L):销毁操作。销毁顺序表,并释放顺序表L所占用的内存空间。

当我们需要对一个传入的参数的值进行修改时,我们传入它的指针,像初始化、删除、插入、销毁这样的操作,我们传入*L;
同理,像删除之后,如果要用e返回,查找到该元素后,要用e返回,我们传入*e

1.InitList_Sq(*L):初始化表。构造一个空的顺序表。

每种方法的写法都有很多种,这里我们都展示一下:

分类一:主函数里声明的是一个指向顺序表的指针
Sqlist *L;

方法一:使用指针的指针

int InitList_Sq(Sqlist **L){ *L=(Sqlist*)malloc(sizeof(Sqlist)); //先给顺序表分配空间(*L)->data=(ElemType *)malloc(sizeof(ElemType)*InitSize); //给数组分配存储空间if(!(*L)->data) return FALSE; //分配失败返回FALSE(*L)->length=0; //空表长度为0(*L)->MaxSize=InitSize;//初始最大长度// 另一种赋值方式// (*(*L)).data=(ElemType *)malloc(sizeof(ElemType)*InitSize);// (*(*L)).length=0;// (*(*L)).MaxSize=InitSize;return TRUE;//成功初始化返回TRUE
}

在主函数种调用:

Sqlist *L;
int i=InitList_Sq(&L);

这种方式写起来很麻烦

方法二:使用return返回值

Sqlist* InitList_Sq(){Sqlist *L; //定义一个顺序表指针,注意不要赋值为空L->data=(ElemType *)malloc(sizeof(ElemType)*InitSize);给数组分配存储空间if(!L->data) return NULL;//分配失败返回NULLL->length=0;//空表长度为0L->MaxSize=InitSize;//初始最大长度return L;//成功初始化返回该指针
}

在主函数种调用:

Sqlist *L;
L=InitList_Sq();

这种方式写起来比上一种简单一点

分类二:主函数里声明的是一个顺序表
Sqlist L;

方法一:使用指针

int InitList_Sq(Sqlist *L){ L->data=(ElemType *)malloc(sizeof(ElemType)*InitSize); //给数组分配存储空间if(!L->data) return FALSE; //分配失败返回FALSEL->length=0; //空表长度为0L->MaxSize=InitSize;//初始最大长度return TRUE;//成功初始化返回TRUE
}

在主函数种调用:

Sqlist L;
int i=InitList_Sq(&L);

个人喜好这种方式,后面的操作采用这种方式进行具体实现

方法二:使用return返回值

Sqlist InitList_Sq(){Sqlist L; L.data=(ElemType *)malloc(sizeof(ElemType)*InitSize);给数组分配存储空间if(!L.data) return;//分配失败L.length=0;//空表长度为0L.MaxSize=InitSize;//初始最大长度return L;//成功初始化
}

在主函数种调用:

Sqlist L;
L=InitList_Sq();

2. Length(L):求表长。

顺序表中的表长很好求得,故不把它写为一个函数,在主函数中直接访问。

SqList *L;
L->length //表长
SqList L;
L.length //表长

3.ListInsert_Sq(*L,i,e):插入操作。在顺序表L中的第i个位置上插入指定元素e。

插入操作示例:

使用指针

int ListInsert_Sq(Sqlist *L,int i,ElemType e){if (i<1||i>L->length+1) return FALSE;//不在插入的有效范围内,返回FALSEif (L->length>=L->MaxSize){ //空间不足,扩容ElemType *new_base=(ElemType *)realloc(L->data,sizeof(ElemType)*(L->MaxSize+ListIncrement));if (!new_base) return FALSE; //分配空间失败L->data=new_base;L->MaxSize+=ListIncrement;}ElemType *q=&(L->data[i-1]);//q为插入的位置ElemType *p;//p为最后一个元素的位置,在for中赋值for(p=&(L->data[L->length-1]);p>=q;p--) *(p+1)=*p; //插入位置之后的元素右移一个*q=e;//插入eL->length++;//表长加一return TRUE;
}

在主函数种调用:

Sqlist L;
ElemType e
int i=ListInsert_Sq(&L,1,e);//在第一个位置上插入e

4.ListDelete_Sq(*L,i,*e):删除操作。删除表中L中第i个位置的元素,并用e返回删除的元素。

删除操作示例:

指针实现

int ListDelete_Sq(Sqlist *L,int i,ElemType *e){   if(i<1||i>L->length) return FALSE;//不在删除的有效范围内ElemType *q=&(L->data[i-1]);//q为删除的元素的位置*e=*q;//把q的值传给eElemType *p=&(L->data[L->length-1]);//p为最后一个元素的位置for(++q;q<=p;++q) *(q-1)=*q; //被删除元素之后的元素左移L->length--;//表长减一
}

在主函数种调用:

Sqlist L;
ElemType e
int i=ListDelete_Sq(&L,1,&e);//删除表中L中第1个位置的元素,并用e返回删除的元素。

5.LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。

这里我们假设每个插入的数据中id的值是唯一的,按值查找时,我们只比较id值

实现

int LocateElem_Sq(Sqlist L,ElemType e){int i;for (i = 0; i <L.length; i++){if(L.data[i].id==e.id) return TRUE;}return FALSE;
}

在主函数种调用:

Sqlist L;
ElemType e
int i=LocateElem_Sq(L,e);//在线性表中查找元素e是否存在

6.GetElem(L,i,*e):按位查找操作。获取表L中第i个位置的元素的值。

实现

int GetElem_Sq(Sqlist L,int i,ElemType *e){if(i<1||i>L.length) return FALSE; //不在有效范围内*e=L.data[i-1];//用e返回查找的值return TRUE;
}

在主函数种调用:

Sqlist L;
ElemType e
int i= GetElem_Sq(&L,1,&e);//获取表L中第1个位置的元素的值。

7.PrintList(L):输出操作。按前后顺序输出线性表L的所有元素值。

实现

void PrintList_Sq(Sqlist L){int i;for (i = 0; i < L.length; i++){printf("第%d行:id=%d,name=%s,description=%s\n",i+1,L.data[i].id,L.data[i].name,L.data[i].description);}
}

在主函数种调用:

Sqlist L;
PrintList_Sq(L);

8.Empty(L):判空操作。

这个对于线性表来说也很好实现,这里也不把它写成函数

在主函数中直接判断:

Sqlist L;
if(L.length==0){printf("空表");
}

9.DestroyList(*L):销毁操作。销毁顺序表,并释放顺序表L所占用的内存空间。

实现

void DestroyList(Sqlist *L){free(L->data);L->length=0;L->MaxSize=0;
}

在主函数种调用:

Sqlist L;
DestroyList(&L);

三、完整代码实现

#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define InitSize 10  //表的初始长度
#define ListIncrement 2  //扩容时的分配增量
typedef struct{int id;//对应表中的idchar name[100];//对应表中的姓名char description[200];//对应表中的描述
}ElemType;//此处的ElemType是个类型名typedef struct{ElemType *data;//数组的基地址,指示动态分配的数组的指针int length;//当前长度int MaxSize;//当前表的最大存储长度
}Sqlist;//顺序表的类型定义/*初始化*/
int InitList_Sq(Sqlist *L){ L->data=(ElemType *)malloc(sizeof(ElemType)*InitSize); //给数组分配存储空间if(!L->data) return FALSE; //分配失败返回FALSEL->length=0; //空表长度为0L->MaxSize=InitSize;//初始最大长度return TRUE;//成功初始化返回TRUE
}/*插入*/
int ListInsert_Sq(Sqlist *L,int i,ElemType e){if (i<1||i>L->length+1) return FALSE;//不在插入的有效范围内,返回FALSEif (L->length>=L->MaxSize){ //空间不足,扩容ElemType *new_base=(ElemType *)realloc(L->data,sizeof(ElemType)*(L->MaxSize+ListIncrement));if (!new_base) return FALSE; //分配空间失败L->data=new_base;L->MaxSize+=ListIncrement;}ElemType *q=&(L->data[i-1]);//q为插入的位置ElemType *p;//p为最后一个元素的位置,在for中赋值for(p=&(L->data[L->length-1]);p>=q;p--) *(p+1)=*p; //插入位置之后的元素右移一个*q=e;//插入eL->length++;//表长加一return TRUE;
} /*删除*/
int ListDelete_Sq(Sqlist *L,int i,ElemType *e){   if(i<1||i>L->length) return FALSE;//不在删除的有效范围内ElemType *q=&(L->data[i-1]);//q为删除的元素的位置*e=*q;//把q的值传给eElemType *p=&(L->data[L->length-1]);//p为最后一个元素的位置for(++q;q<=p;++q) *(q-1)=*q; //被删除元素之后的元素左移L->length--;//表长减一
}/*按值查找*/
int LocateElem_Sq(Sqlist L,ElemType e){int i;for (i = 0; i <L.length; i++){if(L.data[i].id==e.id) return TRUE;}return FALSE;
}/*按位查找*/
int GetElem_Sq(Sqlist L,int i,ElemType *e){if(i<1||i>L.length) return FALSE; //不在有效范围内*e=L.data[i-1];//用e返回查找的值return TRUE;
}/*打印*/
void PrintList_Sq(Sqlist L){int i;for (i = 0; i < L.length; i++){printf("第%d行:id=%d,name=%s,description=%s\n",i+1,L.data[i].id,L.data[i].name,L.data[i].description);}
}/*销毁顺序表*/
void DestroyList(Sqlist *L){free(L->data);L->length=0;L->MaxSize=0;
}int main(void){///初始化Sqlist L;int i=InitList_Sq(&L);if (i==1){printf("初始化成功\n");}else{printf("初始化失败\n"); }printf("\n");
///插入ElemType a[4]={{1,"史强","最强辅助"},{2,"章北海","我不需要钢印,我是自己思想的主人"},{3,"罗辑","人类不感谢罗辑"},{4,"维德","失去人性,失去很多。失去兽性,失去一切"}};int j;for ( j = 0; j < 4; j++){i=ListInsert_Sq(&L,j+1,a[j]);}PrintList_Sq(L);printf("\n");
删除ElemType e;ListDelete_Sq(&L,4,&e);    printf("被删除的元素:id=%d,name=%s,description=%s\n",e.id,e.name,e.description);printf("删除之后:\n");PrintList_Sq(L);printf("\n");
//按值查找i=LocateElem_Sq(L,a[2]);    if (i==1){printf("按值查找成功\n");}else{printf("按值查找失败\n"); }printf("\n");
//按位查找i=GetElem_Sq(L,1,&e);    if (i==1){printf("按位查找成功\n");}else{printf("按位查找失败\n"); }printf("被查找的元素为:id=%d,name=%s,description=%s\n",e.id,e.name,e.description);printf("\n");
//销毁查找       DestroyList(&L);PrintList_Sq(L);//打印为空了
}

运行结果

初始化成功第1行:id=1,name=史强,description=最强辅助
第2行:id=2,name=章北海,description=我不需要钢印,我是自己思想的主人
第3行:id=3,name=罗辑,description=人类不感谢罗辑
第4行:id=4,name=维德,description=失去人性,失去很多。失去兽性,失去一切被删除的元素:id=4,name=维德,description=失去人性,失去很多。失去兽性,失去一切
删除之后:
第1行:id=1,name=史强,description=最强辅助
第2行:id=2,name=章北海,description=我不需要钢印,我是自己思想的主人
第3行:id=3,name=罗辑,description=人类不感谢罗辑按值查找成功按位查找成功
被查找的元素为:id=1,name=史强,description=最强辅助

四、分析操作的复杂度

在分析复杂度之前,我们先介绍一下,顺序表最主要的特点:随机存取

随机存取

如图所示:我们可以通过首地址和元素的序号在时间O(1)内找到指定的元素。
例如:打印时,我们可以直接通过数组下标访问元素.
printf("第%d行:id=%d,name=%s,description=%s\n",i+1,L.data[i].id,L.data[i].name,L.data[i].description);

插入操作

最好情况:在表尾插入,元素无需移动,移动0次,时间复杂度为O(1)。
最坏情况:在表头插入,所有元素都需要向后移动一次,移动n次,时间复杂度为O(n)
平均情况:可以想象,移动次数和插入位置的函数是一个一次函数,平均情况应该为n/2,时间复杂度为O(n)。
综上:时间复杂度为O(n)。

删除操作

最好情况:删除表尾元素,元素无需移动,移动0次,时间复杂度为O(1)。
最坏情况:删除表头元素,除表头外都需要向后移动一次(移动n-1次),时间复杂度为O(n)
平均情况:可以想象,移动次数和删除位置的函数也是一个一次函数,平均情况应该为(n-1)/2,时间复杂度为O(n)。
综上:时间复杂度为O(n)。

按值查找操作

最好情况:查找的元素就在表头,需要比较1次,时间复杂度为O(1)。
最坏情况:查找的元素在表尾或者不存在,需要比较n次,时间复杂度为O(n)
平均情况:可以想象,该函数也是一个一次函数,平均情况应该为(n+1)/2,时间复杂度为O(n)。
综上:时间复杂度为O(n)。

打印操作

打印n次,时间复杂度为O(n)。

其他操作

因为随机存取,时间复杂度为O(1)
例如:按位查找,可以直接根据首地址和元素的序号在时间O(1)内找到指定的元素

五、总结

特点一:逻辑顺序与其物理顺序相同

顺序表是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
因为逻辑相邻物理上也相邻,导致插入和删除操作需要移动大量元素。

特点二:随机存取

对于按位查找,修改某个位置上的值,打印特定位置上的值这些操作很迅速。

特点三:存储密度高

每个结点上只存储数据元素,空间利用率高。

顺序表(线性表的顺序存储)---C语言版相关推荐

  1. c语言实现顺序存储程序,线性表的顺序存储结构动态态分配C语言实现

    线性表的顺序存储结构动态态分配C语言实现 线性表的顺序存储结构动态态分配C语言实现 初始化执行期间通过malloc函数为数组申请空间,程序运行期间若空间不够可通过realloc函数在保留原存储值的前提 ...

  2. 顺序表的c语言结构体描述,顺序表的基本方法实现C语言版

    顺序表--------------线性表的第一个儿子 这个儿子的结构体定义: typedef int ElemType;//取别名 typedef struct link{ ElemType * he ...

  3. 问题 B: 调整表中元素顺序(线性表)

    问题 B: 调整表中元素顺序(线性表) 时间限制: 1 Sec  内存限制: 2 MB 提交: 28  解决: 11 [提交][状态][讨论版] 题目描述 若一个线性表L采用顺序存储结构存储,其中所有 ...

  4. C++实现顺序结构线性表的基本操作

    这两天在准备<软件工程>期末考试,顺带着整理一下今天复习线性表基本操作的代码. ps:本人编程水平一般,有问题还望指出,高手请见谅. main.cpp /* 内容:建立元素数据类型为CEl ...

  5. 逆置线性表(线性表)

    Problem A: 逆置线性表(线性表) Time Limit: 1 Sec   Memory Limit: 128 MB Submit: 228   Solved: 118 [ Submit][ ...

  6. 2201: 逆置线性表(线性表)

    2201: 逆置线性表(线性表) 时间限制: 1 Sec  内存限制: 128 MB 提交: 948  解决: 552 [提交][状态][讨论版][命题人:外部导入] 题目描述 (线性表)请写一个算法 ...

  7. 数据结构笔记(二)--- 顺序实现线性表

    线性结构的顺序实现----设计 一.设计数据类型(D) 假设数据类型为 int 二.设计数据关系(S) 线性,内存连续 三.设计基本操作(P) //1.构造一个空的顺序线性表L void InitLi ...

  8. [数据结构与算法] (顺序)线性表简单demo程序

    1 /******************************************************* 2 * @: Project: (顺序)线性表数据结构演示 3 * @: File ...

  9. 线性表—线性表的合并

    假设有两个集合 A 和 B 分别用两个线性表 LA 和 LB 表示,即线性 表中的数据元素即为集合中的成员. 编写一个算法求一个新的集合 C=A∪B,即将两个集合的并集放在线性表LC中. 解题思路 L ...

  10. 数据结构-广义表详解(类C语言版)

    目录 广义表的概念 定义 表头 表尾 例 广义表的性质 广义表与线性表的区别 广义表的存储结构 头尾链表的存储结构 扩展线性链表的存储结构 ​ 广义表的基本运算 例 广义表的概念 定义 广义表通常记作 ...

最新文章

  1. hadoop,spark,scala,flink 大数据分布式系统汇总
  2. StratifiedShuffleSplit 交叉验证
  3. 关于Myeclipse2017 MemoryAnalyzer的安装
  4. c语言函数 t啥意思,C语言函数大全(t开头)
  5. element table 表格设置max-height 没有出现滚动条,多渲染了一列。
  6. 转:数字对讲机常识介绍
  7. 信息学奥赛一本通C++语言——1030: 计算球的体积
  8. Window上修改了mysql的配置文件my.ini后重启服务报错:本地计算机上的MySQL服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止
  9. 您如何构造适合于numpy排序的数组?
  10. HTML资源嗅探,scrapy-2 嗅探网站,解析HTML
  11. 【Python游戏】Python各大游戏合集:超级玛丽、天天酷跑、我的世界、魔塔、雷霆战机 | 附带源码
  12. 20款优秀的基于浏览器的在线代码编辑器
  13. ILSVRC-ImageNet历年竞赛冠军
  14. html宋体四号字如何设置,宋体小四字体是多少号 首先打开WORD文档,进入界面
  15. 龙门标局:注册地理标志证明商标有什么作用
  16. CREO导出CAD比例不对-解决方法
  17. 电镀废水的来源以及常见的处理方式,各种工艺的讲解
  18. tensorRT onnx中属性名相同,但实际大小不同生成问题
  19. leetcode阶段总结——分割字符串类型
  20. 单例模式和多例模式详解

热门文章

  1. matlab相机标定_综述 | 相机标定方法
  2. linux学习笔记:shell变量
  3. render vue 添加类_详解vue 动态加载并注册组件且通过 render动态创建该组件
  4. vant在cell中加表格_在vant 中使用cell组件 定义图标该图片和位置操作
  5. linux 查看网站目录权限,解决SELinux对网站目录权限控制的不当的问题
  6. mysql 索引 数据页_数据库索引数据页
  7. orm设置bool型 python_详解python的ORM中Pony用法
  8. k8s springboot 文件_用Kubernetes部署Springboot或Nginx,也就一个文件的事
  9. windows 无法停止ics_x64仿真功能加入 ARM版Windows即将获得大量的应用程序
  10. json接口文档模板_在.Net Core WebAPI下给Swagger增加导出离线文档功能