参考:https://blog.csdn.net/jiaoyangwm/article/details/80808235
https://blog.csdn.net/a2392008643/article/details/81781766
https://mp.weixin.qq.com/s/vn3KiV-ez79FmbZ36SX9lg
本文仅是将他人博客经个人理解转化为简明的知识点,供各位博友快速理解记忆,并非纯原创博客,如需了解详细知识点,请查看参考的各个原创博客。
目录
第三章 线性表
线性表(List):由同类型数据元素构成有序序列的线性结构。
元素之间是有顺序的:第一个元素无前驱,最后一个元素无后继,其他元素都有前驱和后继
线性表是有限的
线性表可以利用数组来实现(顺序存储结构),也可以用链表来实现(链式存储结构)。
3.1 线性表的顺序存储结构
顺序存储结构:用一段地址连续的存储单元一次存储线性表的数据元素
顺序存储结构的三个属性:
- 存储空间的起始位置:数组data,它的存储位置就是存储空间的起始位置
- 线性表的最大存储容量:数组长度MaxSize
- 线性表的当前长度:length
![](https://i-blog.csdnimg.cn/blog_migrate/b648cfe78e3c3f66c7560082af6cf8e1.png)
顺序存储结构中
- 获取元素的时间复杂度为O(1)
- 插入元素的平均时间复杂度为O(n)。插入最后一个位置时间复杂度为O(1),插入第一个位置时间复杂度为O(n)
- 删除元素的平均时间复杂度为O(n)。删除最后一个位置时间复杂度为O(1),删除第一个位置时间复杂度为O(n)
3.2 线性表的链式存储结构
链式存储结构:用一组任意的存储单元存储线性表的数据元素。(这组存储单元可以是连续的,也可以是不连续的,即这些数据元素可以存在内存中未被占用的任意位置)
链式存储结构中的每个结点包含:存储数据元素信息的域(数据域)+存储后继元素地址的域(指针域)
- 单链表:每个结点只包含一个指针域的线性链表
- 双向链表:每个结点包含两个指针域,分别指向直接前驱和直接后继
![](https://i-blog.csdnimg.cn/blog_migrate/b1e4eead4ddf7447c9858b64a172a504.png)
- 头指针:头指针是指向链表第一个结点的存储位置的指针,其是链表的必要元素。
- 头结点:第一个结点前设一个结点,可以不存储任何信息,也可以存储长度等附加信息,其是为了操作的统一和方便而设置的,不是链表的必要元素。
![](https://i-blog.csdnimg.cn/blog_migrate/bbfcfc4ed066e1207ed72701b20bf8e6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/89962a5834d071b7849978b43b5beaed.png)
在链式存储结构中:
- 获取元素,需要从头开始找,直到第i个元素未知,时间复杂度为O(n)
- 插入和删除元素,只需要简单改变结点的后继即可,时间复杂度为O(1)
注:无论是顺序结构还是链式结构(逻辑结构),都可以用数组和链表(物理结构)来实现。两者的特点如下:
数组的特点:数组将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素(内存地址固定累加),也就是说,其随机读取效率很高,但插入、删除效率低。同时,数组需要预留空间,在使用前要先申请占内存的大小,可能会浪费内存空间。并且数组不利于扩展,数组定义的空间不够时要重新定义数组。
//声明并初始化一个一维数组
int arr1[m];
memset(arr1, 0, m);
//声明一个多维数组
int arr2[m][n][k];
int *arr3 = new int[m][n][k];
链表的特点:链表的元素在内存中任意存放,通过存在元素中的指针联系到一起。其插入和删除效率高,访问元素效率低。同时,链表不用指定大小,扩展方便,内存利用率高。
//声明链表结点结构体
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
//创建一个结点
ListNode* node = new ListNode(1);
3.3 相关面试题
Q:数组和链表的区别?
A:如上所述。
Q:有什么方法能结合数组和链表的优点?
A:哈希表是数组和链表的折中方案,增加,删除,改写数据的复杂度平均都是O(1),效率非常高。
Q:如何防止数组越界?
A:1)检查传入参数的合法性;2)传参时把数组和数组长度一起传;3)打印数组索引方便检查;4)使用异常捕获。
Q:判断两个链表是否相交?
A:链表相交之后,后面的部分节点全部共用,可以用2个指针分别从这两个链表头部走到尾部,最后判断尾部指针的地址信息是否一样,若一样则代表链表相交。
Q:找出相交链表开始相交的结点?
A:首先计算出两个链表的长度,然后设置两个指针分别指向两个链表,让指向长的链表先走长度的差值步(长链长度-短链长度),此后两个指针一起走,直到找到相等的节点。
Q:判断单链表是否有环?
A:定义快慢指针,同时从链表的头结点出发,快指针每次走两步,慢指针每次走一步。如果快指针和慢指针相遇,则链表有环。此时,若要找出入环结点,可以在相遇后令快指针回到头结点,两个指针每次均走一步,第二次相遇的结点即为入环的第一个结点。
Q:给定一个链表的头指针和结点指针,用O(1)时间删除它?
A:用下一个节点数据覆盖要删除的节点,然后删除下一个节点。但是如果节点是尾节点时,该方法就行不通了。