目录
线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当n=0时线性表是一个空表。若用L命名线性表,则其一般表示为
L=(a1,a2,.....an)
a1是表头元素,an是表尾元素。除a1外都有前驱,除an外都有后继
线性表是一种逻辑结构,表示元素之间一对一的相邻关系。
线性表包括顺序表和链表,顺序表里面元素的地址是连续的。链表里面节点的地址不一定连续的,是通过指针连起来的。顺序表和链表是指存储结构。
InitList(&L):初始化线性表
Length(L):求表长
LocateElem(L,e):按值查找操作
GetElem(L,i):按位查找操作
ListInsert(&L,i,e):插入操作
ListDelete(&L,i,&e):删除操作
PrintList(L):输出操作
Empty(L): 判空操作
DestroyList(&L):销毁操作
注意:&表示c语言中的引用调用,在c语言中用指针也可达到同样效果。
请参考
c语言 引用调用_C中按值调用和按引用调用之间的区别_culing2941的博客-CSDN博客
线性表的顺序存储又称顺序表,逻辑地址上相邻的地址元素,物理地址也相邻。
特点:表中元素逻辑顺序与物理顺序相同,用数组实现的,一维数组可以是静态的,也可以是动态的。
- 静态分配
- #define MaxSize 50//定义线性表最大长度
- typedef struct{
- ElemType data[MaxSize];//顺序表元素
- int length;//顺序表当前长度
- }SqList//顺序表类型定义
-
-
- 动态分配
- #define InitSize 100//初始化表长度
- typedef struct{
- ElemType *data;//指示动态分配数组的指针
- int MaxSize,length;//数组最大容量和当前个数
- }SeqList//类型定义
-
- 动态分配初始化语句:
- c: L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
- c++: L.data=new ElemType[InitSize];
- 注:动态分配不是链式存储,他是顺序存储结构,物理结构并没有发生变化,依然是随机存取方式,只是分配的空间大小可以在运行时动态决定。
注:线性表从1开始,而数组从0开始。
优点:随机访问特性,查找O(1)时间,存储密度高;逻辑上相邻的元素,物理上也相邻;
缺点:插入删除需移动大量元素。
插入操作:
最好情况:表尾插入,时间复杂度o(1)
最坏情况:表头插入,时间复杂度o(n)
平均情况:o(n/2)=o(n)
删除操作:
最好情况:表尾删除,时间复杂度o(1)
最坏情况:表头删除,时间复杂度o(n)
平均情况:o(n/2)=o(n)
查找操作:
- int LocateElem(SqList L,ElemType e){
- int i;
- for(i=0;i<L.length;i++{
- if(L.data[i]==e)
- return i+i;//返回其位序
- return 0;//查找失败
- }
最好情况:表头查找,时间复杂度o(1)
最坏情况:表尾查找或查找失败,时间复杂度o(n)
平均情况:o(n/2)=o(n)
与顺序存储相比,允许存储空间不连续,插入删除时不需要移动大量的元素,只需修改指针即可,但查找某个元素,只能从头遍历整个链表。且不能随机存取。
定义:
单链表分为带头结点和不带头结点两种,不管有没有头结点,头指针都指向链表的第一个节点(头结点是第一个结点)。
头结点:数值域可不设任何信息,头结点的指针域指向链表的第一个元素。
带头节点的好处有:
(1)链表第一位置节点上的操作和其它位置上的操作一致
(2)无论链表是否为空,头指针都指向头结点(非空),空表和非空表处理一样
1.头插法建立新链表
读入数据顺序与生成链表元素顺序是相反的。每个节点插入时间为o(1),设单链表长为n,则总时间复杂度为o(n)
如果没有设立头结点,则需要在每次插入新节点后,将他的地址赋值给头指针L。
2.尾插法
- LinkList List_TailInsert(LinkList &L){
- int x;
- L=(LinkList)malloc(sizeof(LNode));
- LNode *s,*r=L;//r为尾指针
- scanf("%d",&x);//输入结点值
- while(x!=9999){//9999表示结束
- s=(LNode *)malloc(sizeof(LNode));
- s->data=x;
- r->next=s;
- r=s;//r指向新的表尾指针
- scanf("%d",&x);
- }
- r->next=NULL;//尾指针指置空
- return L;
- }
- 时间复杂度与头插法相同
3.按序号查找结点值
时间复杂度o(n)
4.按值查找表结点
时间复杂度o(n)
5.插入结点操作
本算法主要时间开销在查找i-1元素上,时间复杂度为o(n),若结点已给定,则在其后插入结点时间复杂度为o(1)。
6.删除结点操作
时间复杂度为o(n)
7.求表长操作
由于单链表插入删除都得从头遍历,比较麻烦,时间复杂度高。所以我们引入了双链表来解决
主要就是结点中有两个指针,分别指向前驱和后继
- typedef struct DNode{
- ElemType data;//数据域
- struct DNode *prior,*next;//前驱后继指针
- }DNode, *DLinkList;
由于引入了双指针,所有我们的插入删除时间复杂度变为了o(1)
1.插入
2.删除
1.循环单链表
引用这种后,我们对表头表尾操作的时间复杂度都变为了o(1)
2.循环双链表
借助数组来描述线性表的链式存储结构,结点也有数据域和指针域。
左边为静态链表示例,右图为静态链表对应的单链表
- #define MaxSize 50//定义静态链表的最大长度
- typedef struct{
- ElemType data;//存储数据类型
- int next;//下一个元素的数组下标
- }SLinkList[MaxSize];