顺序表(线性表的顺序存储)---C语言版
目录
- 顺序表
- 一、顺序表的定义
- 二、顺序表上基本操作的具体实现
- 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语言版相关推荐
- c语言实现顺序存储程序,线性表的顺序存储结构动态态分配C语言实现
线性表的顺序存储结构动态态分配C语言实现 线性表的顺序存储结构动态态分配C语言实现 初始化执行期间通过malloc函数为数组申请空间,程序运行期间若空间不够可通过realloc函数在保留原存储值的前提 ...
- 顺序表的c语言结构体描述,顺序表的基本方法实现C语言版
顺序表--------------线性表的第一个儿子 这个儿子的结构体定义: typedef int ElemType;//取别名 typedef struct link{ ElemType * he ...
- 问题 B: 调整表中元素顺序(线性表)
问题 B: 调整表中元素顺序(线性表) 时间限制: 1 Sec 内存限制: 2 MB 提交: 28 解决: 11 [提交][状态][讨论版] 题目描述 若一个线性表L采用顺序存储结构存储,其中所有 ...
- C++实现顺序结构线性表的基本操作
这两天在准备<软件工程>期末考试,顺带着整理一下今天复习线性表基本操作的代码. ps:本人编程水平一般,有问题还望指出,高手请见谅. main.cpp /* 内容:建立元素数据类型为CEl ...
- 逆置线性表(线性表)
Problem A: 逆置线性表(线性表) Time Limit: 1 Sec Memory Limit: 128 MB Submit: 228 Solved: 118 [ Submit][ ...
- 2201: 逆置线性表(线性表)
2201: 逆置线性表(线性表) 时间限制: 1 Sec 内存限制: 128 MB 提交: 948 解决: 552 [提交][状态][讨论版][命题人:外部导入] 题目描述 (线性表)请写一个算法 ...
- 数据结构笔记(二)--- 顺序实现线性表
线性结构的顺序实现----设计 一.设计数据类型(D) 假设数据类型为 int 二.设计数据关系(S) 线性,内存连续 三.设计基本操作(P) //1.构造一个空的顺序线性表L void InitLi ...
- [数据结构与算法] (顺序)线性表简单demo程序
1 /******************************************************* 2 * @: Project: (顺序)线性表数据结构演示 3 * @: File ...
- 线性表—线性表的合并
假设有两个集合 A 和 B 分别用两个线性表 LA 和 LB 表示,即线性 表中的数据元素即为集合中的成员. 编写一个算法求一个新的集合 C=A∪B,即将两个集合的并集放在线性表LC中. 解题思路 L ...
- 数据结构-广义表详解(类C语言版)
目录 广义表的概念 定义 表头 表尾 例 广义表的性质 广义表与线性表的区别 广义表的存储结构 头尾链表的存储结构 扩展线性链表的存储结构 广义表的基本运算 例 广义表的概念 定义 广义表通常记作 ...
最新文章
- hadoop,spark,scala,flink 大数据分布式系统汇总
- StratifiedShuffleSplit 交叉验证
- 关于Myeclipse2017 MemoryAnalyzer的安装
- c语言函数 t啥意思,C语言函数大全(t开头)
- element table 表格设置max-height 没有出现滚动条,多渲染了一列。
- 转:数字对讲机常识介绍
- 信息学奥赛一本通C++语言——1030: 计算球的体积
- Window上修改了mysql的配置文件my.ini后重启服务报错:本地计算机上的MySQL服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止
- 您如何构造适合于numpy排序的数组?
- HTML资源嗅探,scrapy-2 嗅探网站,解析HTML
- 【Python游戏】Python各大游戏合集:超级玛丽、天天酷跑、我的世界、魔塔、雷霆战机 | 附带源码
- 20款优秀的基于浏览器的在线代码编辑器
- ILSVRC-ImageNet历年竞赛冠军
- html宋体四号字如何设置,宋体小四字体是多少号 首先打开WORD文档,进入界面
- 龙门标局:注册地理标志证明商标有什么作用
- CREO导出CAD比例不对-解决方法
- 电镀废水的来源以及常见的处理方式,各种工艺的讲解
- tensorRT onnx中属性名相同,但实际大小不同生成问题
- leetcode阶段总结——分割字符串类型
- 单例模式和多例模式详解
热门文章
- matlab相机标定_综述 | 相机标定方法
- linux学习笔记:shell变量
- render vue 添加类_详解vue 动态加载并注册组件且通过 render动态创建该组件
- vant在cell中加表格_在vant 中使用cell组件 定义图标该图片和位置操作
- linux 查看网站目录权限,解决SELinux对网站目录权限控制的不当的问题
- mysql 索引 数据页_数据库索引数据页
- orm设置bool型 python_详解python的ORM中Pony用法
- k8s springboot 文件_用Kubernetes部署Springboot或Nginx,也就一个文件的事
- windows 无法停止ics_x64仿真功能加入 ARM版Windows即将获得大量的应用程序
- json接口文档模板_在.Net Core WebAPI下给Swagger增加导出离线文档功能