链表与顺序表的本质区别
在数据结构中,顺序表和链表是两种基本的线性表实现方式,它们的核心区别在于存储方式:
- 顺序表:元素存储在连续的内存单元中,通过索引直接访问
- 链表:元素可以存储在任意的内存单元中(连续或不连续),通过指针将分散的节点串联成逻辑上连续的序列
链表的基本构成
链表的最小单元是节点,每个节点包含两部分:
- 数据域:存储节点本身的数据
- 指针域:存储上一个或下一个节点的内存地址,使节点之间能够相互关联
单链表
单链表是最简单的链表结构,每个节点只有一个指针域,指向其后继节点。尾节点的next指针一定是NULL,且无法逆向访问(不能回头)。
单链表的基本操作
1. 头插法
头插法是将新节点插入到链表的头部:
- 新节点的next指针指向当前头节点
- 更新头指针指向新节点
- 特点:插入顺序与最终链表顺序相反
2. 尾插法
尾插法是将新节点插入到链表的尾部:
- 找到当前尾节点(next指针为NULL的节点)
- 将尾节点的next指针指向新节点
- 新节点的next指针设为NULL
- 特点:插入顺序与最终链表顺序相同
3. 在指定位置插入数据
- 遍历链表,找到要插入位置的前一个节点
- 新节点的next指针指向插入位置的原节点
- 前一个节点的next指针指向新节点
4. 删除节点
- 找到要删除节点的前一个节点p
- 用指针q记录要删除的节点(p->next)
- 将p的next指针指向q的next节点(跳过q)
- 释放q节点的内存空间
5. 获取链表长度
从头节点开始遍历整个链表,每访问一个节点就计数加1,直到遇到尾节点(next为NULL)。
6. 释放链表
释放链表需要逐个释放所有节点:
- 让指针p指向头节点后的第一个节点
- 循环:
- 用指针q记录p的后继节点
- 释放p指向的节点
- 让p指向q
- 直到p为NULL
7. 双指针技巧
双指针技术在链表操作中非常实用,可用于:
- 寻找链表的中间节点
- 寻找倒数第k个节点
- 判断链表是否有环等场景
基本思路是使用两个指针,根据需求以不同速度或不同起点移动。
单向循环链表
单向循环链表与单链表的区别在于:最后一个节点的指针不是指向NULL,而是指向头节点,形成一个闭环。
单向循环链表的特点
- 遍历结束条件与单链表不同:
- 单链表:
p != NULL或p->next != NULL - 单向循环链表:
p != L或p->next != L(L为头节点)
- 单链表:
环相关问题
1. 如何判断链表有环?
使用快慢指针法:
- 设置两个指针,慢指针每次走1步,快指针每次走2步
- 同时从链表头节点出发
- 如果链表中存在环,快指针会在环内"追上"慢指针(两者指向同一节点)
- 如果快指针先到达NULL,则链表无环
2. 如何找到环的入口?
- 先通过快慢指针确定链表有环
- 记录快慢指针相遇的节点
- 将慢指针重置到链表头节点
- 快慢指针以相同速度(每次1步)前进
- 两指针再次相遇的节点就是环的入口
双向链表
双向链表的每个节点有两个指针域:
- 一个指向直接后继节点
- 一个指向直接前驱节点
这种结构允许双向遍历,提高了某些操作的效率,但也增加了一定的存储空间开销。
双向链表的基本操作
1. 头插法
- 新节点的next指针指向当前头节点的next
- 新节点的prev指针指向头节点
- 若头节点的next不为NULL,更新其prev指针指向新节点
- 头节点的next指针指向新节点
2. 尾插法
- 找到当前尾节点(通常可维护一个尾指针直接获取)
- 新节点的prev指针指向尾节点
- 新节点的next指针设为NULL
- 尾节点的next指针指向新节点
- 更新尾指针指向新节点
3. 指定位置插入数据
需要同时维护前驱和后继指针的指向关系,确保双向引用正确。
4. 删除节点
- 找到要删除的节点p
- p的前驱节点的next指针指向p的后继节点
- 若p的后继节点不为NULL,其prev指针指向p的前驱节点
- 释放p节点的内存空间
顺序表与链表的对比
| 特性 | 顺序表 | 链表 |
|---|---|---|
| 存储空间 | 连续 | 不连续 |
| 访问效率 | 高(随机访问,O(1)) | 低(顺序访问,O(n)) |
| 插入删除效率 | 低(需移动元素,O(n)) | 高(只需修改指针,O(1)) |
| 空间分配 | 静态分配,可能有内存浪费或溢出 | 动态分配,按需分配 |
| 存储密度 | 高(只需存储数据) | 低(需额外存储指针) |
| 适用场景 | 频繁访问,元素数量固定 | 频繁插入删除,元素数量动态变化 |
简言之,顺序表是"以空间换时间",链表是"以时间换空间"

1万+

被折叠的 条评论
为什么被折叠?



