目录
- 2.4.8 静态链表
- 2.4.9 顺序表和链表的比较
- 2.4.9.1 逻辑结构
- 2.4.9.2 存储结构
- 2.4.9.3 基本操作 - 创建
- 2.4.9.4 基本操作 - 销毁
- 2.4.9.5 基本操作-增/删
- 2.4.9.6 基本操作-查
- 2.4.9.7 顺序、链式、静态、动态四种存储方式的比较
- 2.4.9.8 存储密度的问题
- 2.4.9.9 存储方式的选择以及二者优劣的答题思路(综述优劣,细谈情况)
2.4.8 静态链表
- 定义:
单链表:各个结点散落在内存中的各个角落,每个结点有指向下一个节点的指针(下一个结点在内存中的地址);
静态链表:用数组的方式来描述线性表的链式存储结构: 分配一整片连续的内存空间,各个结点集中安置,包括了——数据元素and下一个结点的数组下标(游标)
其中数组下标为0的结点充当"头结点"
游标为-1表示已经到达表尾
游标为-2表示该位置为空,没有数据且没有游标指向这个位置
若每个数据元素为4B,每个游标为4B,则每个结点共8B;假设起始地址为addr,则数据下标为2的存放地址为:addr+8*2
注意: 数组下标——物理顺序,位序——逻辑顺序;
优点:增、删操作不需要大量移动元素;
缺点:不能随机存取,只能从头结点开始依次往后查找,容量固定不变!
-
几种写法:
注意:SLinkList a 强调a是静态链表;struct Node a 强调a是一个Node型数组;
-
静态链表基本操作的实现
-
初始化静态链表:把a[0]的next设为-1
-
查找某个位序(不是数组下标,位序是各个结点在逻辑上的顺序)的结点:从头结点出发挨个往后遍历结点,时间复杂度O=(n)
-
在位序为i上插入结点:① 找到一个空的结点,存入数据元素;② 从头结点出发找到位序为i-1的结点;③修改新结点的next;④ 修改i-1号结点的next;
-
删除某个结点:① 从头结点出发找到前驱结点;② 修改前驱节点的游标;③ 被删除节点next设为-2;
2.4.9 顺序表和链表的比较
2.4.9.1 逻辑结构
- 顺序表和链表都属于线性表,都是线性结构
2.4.9.2 存储结构
-
顺序表:顺序存储
优点:支持随机存取,存储密度高
缺点:大片连续空间分配不方便,改变容量不方便 -
链表:链式存储
优点:离散的小空间分配方便,改变容量方便
缺点:不可随机存取,存储密度低
2.4.9.3 基本操作 - 创建
-
顺序表:需要预分配大片连续空间。若分配空间过小,则之后不方便拓展容量;若分配空间过大,则浪费内存资源;
-
静态分配:静态数组,容量不可改变
-
动态分配:动态数组,容量可以改变,但是需要移动大量元素,时间代价高(malloc(),free())
-
链表:只需要分配一个头结点或者只声明一个头指针
2.4.9.4 基本操作 - 销毁
顺序表:
首先修改 Length = 0
- 静态数组——系统自动回收空间
typedef struct{
ElemType *data;
int MaxSize;
int length;
}SeqList;
创建的时候静态建立没有malloc分配空间。
- 动态分配:动态数组——需要手动free(),一个malloc对应一个free,成对出现
//创
L.data = (ELemType *)malloc(sizeof(ElemType) *InitSize)
//销
free(L.data);
//!malloc() 和 free() 必须成对出现
2.4.9.5 基本操作-增/删
-
顺序表:插入/删除元素要将后续元素后移/前移;时间复杂度=O(n),时间开销主要来自于移动元素;
-
链表:插入/删除元素只需要修改指针;时间复杂度=O(n),时间开销主要来自查找目标元素
需要注意的是:在单个元素占用空间很大的情况下,顺序表增删元素的时间开销很大(因为主要的时间开销不在查找元素而是在移动元素,并且移动的元素很大),而链表的时间开销仍能保持在一个较小的数值(因为链表主要的时间开销在于移动指针寻找节点,所以才节点大小很大的时候仍能保证一定的速度)。
此时时间开销他们二者虽然都是O(n)但是在面对较多的增删操作的情况下一般会选择链表实现。
2.4.9.6 基本操作-查
-
顺序表
按位查找:O(1)
按值查找:O(n),若表内元素有序,可在O(log2n)时间内找到 -
链表
按位查找:O(n)
按值查找:O(n)
2.4.9.7 顺序、链式、静态、动态四种存储方式的比较
- 顺序存储的固有特点:
逻辑顺序与物理顺序一直,本质上是用数组存储线性表的各个元素(即随机存取);存储密度大,存储空间利用率高。 - 链式存储的固有特点:
元素之间的关系采用这些元素所在的节点的“指针”信息表示(插、删不需要移动节点)。 - 静态存储的固有特点:
在程序运行的过程中不要考虑追加内存的分配问题。 - 动态存储的固有特点:
可动态分配内存;有效的利用内存资源,使程序具有可扩展性。
2.4.9.8 存储密度的问题
-
存储密度:
在数据结构中,结点数据本身所占的存储量和整个结点结构所占的存储量之比。
存储密度 = 结点数据本身所占存储量 / 整个结点结构所占的存储量 -
顺序表的存储密度等于1
-
单链表的存储密度小于1
-
假设单链表的结点的数据占的存储量为N,结点的指针域所占的存储量为M,则存储密度 = N / (N+M),所以单链表的密度是小于1的。
2.4.9.9 存储方式的选择以及二者优劣的答题思路(综述优劣,细谈情况)