文章目录
逻辑结构 :数据元素之间抽象的前后关系
物理/存储结构 (存储映像):内存中真实存放形式
运算 :查找、插入、删除、随机访问、遍历等
逻辑结构
结论:两者的逻辑结构完全相同。
- 逻辑结构 :
都是线性结构,即数据元素之间具有 "一对一" 的线性关系。 - 区别:无区别。逻辑结构只描述数据之间的抽象关系,不涉及存储方式。无论是顺序表还是链表,它们在逻辑上都是一条"线"。
物理结构 / 存储结构
顺序表(顺序存储)
- 内存:一片
连续的存储单元 - 元素相邻,内存地址相邻
- 通过下标直接定位元素,依靠首地址 + 偏移量寻址
- 存储密度:高(
=1),只存储数据,无指针开销
优点:支持随机存取,存储密度高。
缺点:大片连续空间分配不方便,改变容量不方便。
链表(链式存储)
- 内存:物理离散,
结点分散、不要求连续 - 结点 = 数据域 + 指针 / 游标域
- 依靠指针找到下一个结点;无法通过简单计算直接定位
- 结点之间靠指针维系逻辑相邻,物理地址无关
- 存储密度:低(
<1),每个结点除了存储数据,还要存储指针
优点:离散的小空间分配方便,改变容量方便。
缺点:不可随机存取,存储密度低。
静态链表
物理上在连续数组内,但逻辑依靠游标链接;物理连续,逻辑链式,归类为链表。
数据运算
创建(初始化)
顺序表(顺序存储):
- 需要预分配大片连续空间。若分配空间过小,则之后不方便拓展容量;若分配空间过大,则浪费内存资源。
- 静态分配:静态数组(容量不可改变)
- 动态分配:动态数组(malloc、free)(容量可改变,但需要移动大量元素,时间代价高)
链表(链式存储):优
- 只需分配一个头结点(也可以不要头结点,只声明一个头指针),之后方便拓展。
销毁
顺序表(顺序存储):O(1)
- 静态分配:静态数组(系统自动回收空间)
- 动态分配:动态数组(
malloc、free)【malloc和free必须成对出现】(malloc在堆区,不能自动回收空间,所以需要手动free)
链表(链式存储):O(n)
- 需要写循环,逐个遍历释放。必须从头开始,依次
free每一个数据结点。
插入 与 删除
顺序表(顺序存储):
- 插入 / 删除元素要将后续元素都后移 / 前移;
- 时间复杂度:
O(n),时间开销主要来自移动元素; - 若数据元素很大,则移动的时间代价更高。
链表(链式存储):优
- 插入 / 删除元素只需要修改指针即可;
- 时间复杂度:
O(n),时间开销主要来自查找目标元素; - 查找元素的时间代价更低。
查找
顺序表(顺序存储):优
- 按位查找:
O(1) - 按值查找:
O(n),若表内元素有序,可在 O(log2n) 时间内找到
链表(链式存储):
- 按位查找:
O(n) - 按位查找:
O(n)
总结
| 比较维度 | 顺序表(数组) | 链表(指针) |
|---|---|---|
| 逻辑结构 | 线性结构(一对一) |
线性结构(一对一) |
| 物理结构 | 连续存储 |
离散存储(通过指针链接) |
| 存储密度 | 高(≈1) |
低(<1,需存指针,单链表 4/8 字节,双链表 8/16 字节) |
| 创建(初始化) | 一次性分配大块空间,length=0,需预估最大容量(O(1)) |
只分配头结点(或设为 NULL),无需预估容量(O(1)) |
| 销毁(释放) | 一次释放(O(1)),简单快速 |
逐个释放(O(n)),遍历慢,极易内存泄漏 |
| 随机访问(按位) | O(1)(优势) |
O(n)(劣势) |
| 按值查找(有序) | O(log~2~n)(二分查找) |
O(n)(无法二分,只能遍历) |
| 插入/删除(已知位置) | O(n)(需移动元素) |
O(1)(优势,只改指针) |
| 插入/删除(按位序) | O(n)(移动元素) |
O(n)(遍历找前驱) |
| 表头插入/删除 | O(n)(需移动所有元素) |
O(1)(优势) |
| 表尾插入/删除(含尾指针) | O(1)(若未满) |
插入 O(1),删除 O(n)(单向链表找不到尾前驱) |
| 适用场景 | "查"多"改"少、数据量稳定、需频繁随机访问 | "增"多"删"多、数据量动态变化、不关心随机访问 |