线性表知识盘点

在《数据结构》里我们最先接触的就是线性表,线性表作为一种较为简单的结构,我们更应该完全掌握。


知识框架

在这里插入图片描述
在本书里我们学的所有数据结构一般都可以分为两大类:
1.一类是线性结构
2.另一类则是链式结构
线性结构和链式结构的本质区别就在于其在内存里面的存取方式的不同。

线性结构

线性结构的存储特点就是:所有的数据在内存里都是相邻的。在物理结构上的意思是你可以把10个数据保存在10个连续的物理地址,比如从0X00000001到0X0000000A;在逻辑结构上就是一种类似“串行”的线性结构,并且按照物理结构上的顺序相连。

链式结构

链式结构的存储特点是:在物理结构上并无区别,但是在逻辑结构上并不是按照顺序存取的,我们可以用“指针”来自定存取的下一位,比如原本0X00000001的下一位是0X00000002,但是我们可以使用指针将下一位指向0X00000003。因此在逻辑上虽然也是串行的结构,但是我们可以直接根据指定的下一位地址而非其在内存中原本的顺序来进行存取。

(当然这些只是简单的描述,具体还需学习计算机组成和操作系统)


线性表的定义和操作

线性表的定义

线性表是指具有相同数据类型的n个数据元素所构成的有限序列。
比如 L =(a1,a2…an),我们将a1称作表头元素,an称作表尾元素
因为每个元素都是相同类型的,这就代表每个元素所占的空间大小也是相同的

线性表的操作

InitList(&L)初始化表
Length(L)求表长
LocateElem(L,e)按值查找,查找表L中值为e的元素
GetElem(L,i)按位查找,查找表L中第i位的元素的值
ListInsert(&L,i,e)插入操作,把值e插入到表L的第i位元素
ListDelete(&L,i,&e)删除操作,删除表L中第i位元素值并返回给e(直接删除ListDelete(&L,i))
PrintList(L)输出表格元素
Empty(L)判空操作,判断L是否为空表
DestoryList(&L)销毁操作,消耗表L,一般在使用完后不再使用则销毁返还所占用内存空间

线性表的存储类型

typedef struct{
	ElemType data[Maxsize];
	int Length;
}SqList;  //定义的结构名
或者
typedef struct SqList{
	ElemType data[Maxsize];
	int Length;
}List1,List2; //定义两个SqList类型结构

上述是静态分配的数组data,固定了存储空间大小位Maxsize,但是有些题目需要我们对数组进行动态分配

typedef struct{
	ElemType *data; //一个指向动态分配数组的指针,其空间不定,它将指向我们随后分配的空间
	int Length;
}SqList; 
或者
L.data=(ElemType *)malloc(sizeof(ElemType)*Initsize);
用malloc语句来分配动态数组

动态分配并不代表其在内存的物理结构改变,依然是顺序存储结构,但是可以动态决定分配的空间大小


顺序表上基本操作的实现

基本操作就是插入删除修改查找,由于顺序结构不便修改,只讲具体思路

在顺序表中,由于是顺序存储的,一般情况下我们插入删除都是在表尾进行的,需要有一个指针来指向表尾的位置。
如果想要在任意位置进行插入和删除,同样我们需要找到操作的位置,插入就是将其后的元素整体向后移动一格,然后空出那个位置把我们需要插入的元素放进去
删除则是把我们寻找的那格删除之后再将后面的元素整体前移一格。

所以在线性表里面如果要按指定位置插入删除是十分麻烦的,对于需要频繁进行更改的数据结构,我们要考虑不使用这种线性结构,而是采用链式结构,用指针来表示的空间顺序在修改上会更加简便。


线性表的链式结构

如果在顺序表里进行插入删除操作,需要移动大量的元素,十分困难。
因此我们可以使用链式结构来解决这个问题
链式结构的主要特点是,虽然在逻辑上这些数据是相邻的,但是他们在空间上不一定是相邻的,因此也不需要连续的存储单元。由于连续的存储单元比较少,而且需要进行插入删除操作,因此大部分情况下链式结构的存储方式更适合我们使用。(包括各种数据结构类型)唯一比不上顺序表的地方就是失去的随机存取的特点。

链式存储的结构也有很多种,本节我们主要学习的有单链表和双链表,循环链表,静态链表,动态链表等。

单链表的定义

在这里插入图片描述
单链表的结构可以分为两块,一块用于存储数据,称为数据域,另一块用于存放指针,称为指针域。

typedef struct LNode{
	ElemType data; 
	struct LNode *next; //指针类型要和指向的结构类型一样
}LNode, *LinkList;

单链表的缺陷是不能直接找到表中某个特定的结点,必须从头开始遍历(除非你在组织结构的时候就已经把他们按照一定的规律排序,或者按照一定的条件来查找)

另外,我们通常需要一个头结点来指向表头
在这里插入图片描述
当头结点指向为Null说明指向的是一个空表(斜线代表头结点,其内不存数据,L代表头指针,指向这个链表)

单链表在操作上的实现

单链表的操作通常用头插法和尾插法来完成,头插法就是把要操作的数据放在头结点前完成。
比如要把ai插入到链表,用头插法就是把他插入到的第一个结点前头(注意第一个结点是头结点后面的结点而不是头结点),

ai->next=a1;
head->next=ai;

或者我们也可以用尾插法,用一个尾结点来指向尾部,然后把输入插入到尾部的后面

tail->next=ai;
ai->next=NULL;
tail=ai;

其余的操作比如把ai插入到a2a3之间

ai->next=a3;
a2->next=ai;

或者把a3从a2和a4之间删除

a2->next=a4; //直接将a2的next设置为a4,在逻辑上删除
a3.free();	//释放a3的空间,在物理上删除

时间有限,其实这些也没什么好讲的,太基础了


双链表

双链表就是在单链表的基础上多了一个指针域,这个指针存放的是指向前一个数据的指针。
在这里插入图片描述
双链表的好处在于可以完善单链表所不能实现的查找功能,单链表的查找只能向后查找,而双链表既可以向前,也可以向后,十分灵活。

删除插入修改这些具体操作先不写了,比较简单基础,有时间再补充


循环链表

循环链表就是实现了逻辑结构上的循环的链表,其实就是把尾指针的next不是指向NULL而是指向了头指针,这样尾指针的下一个就是头指针,相当于循环转了一圈又绕回来了。
单链表和双链表的结构都能实现循环链表


静态链表和动态链表

静态链表就是像顺序表一样,我们静态地分配一个固定大小的空间,那么这个链表所占的空间大小就不能超过我们分配的空间大小,当然虽然是连续的空间块,我们的数据存放也是无顺序的。
静态链表一般以next==-1为结束的标志
动态链表应该也没这个说法,其实就是我们正常创建的链表,由于链表存储空间本来就不连续,所以也不固定,无所谓动态之说。


顺序表和链表的比较

1.存取(读写)方式
顺序表可以顺序存取,也可以随机存取(之所以能随机存取是因为顺序表的空间地址=locate+sizeof(ElemType)*n,随机取任意一块数据,计算机就可以按照这个公式来算出属于这个顺序表的任意一个数据的地址),
而链表只能从表头顺序存取元素,比如第i个数据需要经过i次next的循环,需要访问i次,而顺序表只需1次。

2.逻辑结构与物理结构
顺序表在逻辑上和物理上都是相邻的,而链式存储时虽然逻辑上相邻,但是物理上却不一定相邻。

3.查找,插入和删除
顺序表的插入和删除较为复杂,用链表来进行插入删除是更佳的选择。
按值查找两者复杂度都是一样的,而按序号查找时由于顺序表可以随机存取,所以时间复杂度是O(1),而链表时间复杂度是O(n).

4.空间分配
顺序表需要我们预先分配足够大的空间,否则溢出。如果要实现顺序表的动态存储则需要扩容,但是也不方便我们随时修改。
链表数据本身是不连续存储的,所以本来就是动态的,在空间分配上要灵活很多。


总结

对于动态性比较强,情况复杂的情景我们通常采用链表
而静态的,比较简单的情景我们可以采用顺序表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值