链表(Linked List)是 Leetcode 和数据结构面试中非常重要的基础知识之一。我们会从概念、种类、基本操作、经典题型、使用时机等多个维度详细讲解。
链表是一种线性数据结构,每个元素是一个节点(Node
),节点包含两部分:
-
值(value)
-
指针(next):指向下一个节点的引用
不像数组,链表中的元素不连续存储,适合频繁插入/删除的场景。
1.链表的类型对比
类型 | 特点 | 示例用途 |
---|---|---|
单向链表 | 每个节点只指向下一个 | 经典链表题,基础结构 |
双向链表 | 每个节点指向前后两个节点 | LRU 缓存等需要双向访问的场景 |
循环链表 | 尾节点指向头节点形成环 | 判环、约瑟夫环问题 |
虚拟头节点 | 头节点前添加 dummy 节点统一处理逻辑 | 统一处理插入、删除操作 |
2.链表常见操作
2.1. 遍历链表:
def traverse(head):
while head:
print(head.val)
head = head.next
2.2. 插入节点:
new_node = ListNode(5)
new_node.next = node.next
node.next = new_node
2.3. 删除节点:
node.next = node.next.next
3.Leetcode 中链表典型应用场景
3.1. 反转链表:双指针 + 修改指向
-
题目:206. Reverse Linked List
-
技巧:使用
prev
和curr
逐个反转
3.2. 检测环:快慢指针
-
题目:141. Linked List Cycle
-
技巧:
slow
每次走一步,fast
每次走两步,相遇则有环
3.3. 合并链表:归并思想
-
题目:21. Merge Two Sorted Lists
-
技巧:递归或 dummy 节点迭代
3.4. 找中点 or 倒数第 k 个节点:快慢指针
-
题目:876. Middle of the Linked List
-
技巧:slow 一步,fast 两步,fast 到尾时 slow 正好在中点
3.5. 删除节点:双指针
-
题目:19. Remove Nth Node From End of List
-
技巧:先让 fast 走 n 步,再一起走
4.如何判断用不用链表?
题目描述包含 | 使用链表的可能性 |
---|---|
"节点"、"指针"、"引用" | ✅ 非常可能 |
"倒数第 K 个"、"中间节点" | ✅ 快慢指针技巧常用 |
"合并两个链表"、"删除节点" | ✅ 模板题 |
涉及频繁插入/删除、顺序结构 | ✅ 比数组更优 |
明确给出 ListNode 类 | ✅ 必须使用链表 |
要求 O(1) 插入/删除 | ✅ 链表优于数组 |
需要索引访问 (如 nums[3]) | ❌ 链表不合适 |
5.Leetcode 高频链表题
题号 | 题目 | 技巧关键词 |
---|---|---|
206 | 反转链表 | 双指针 |
21 | 合并两个有序链表 | dummy + 遍历 |
141 | 判断链表是否有环 | 快慢指针 |
160 | 相交链表 | 双指针 + 同步尾部 |
19 | 删除倒数第 N 个节点 | dummy + 快慢指针 |
234 | 回文链表 | 找中点 + 反转比较 |
876 | 链表中点 | 快慢指针 |
25 | 每 k 个一组反转链表 | 分组递归反转 |
【LeetCode 21】合并俩个有序列表Merge Two Sorted Lists
给定两个升序链表 list1
和 list2
,合并它们成为一个新的升序链表,并返回新链表的头节点。
核心思路:
-
创建一个虚拟头
dummy
,用指针current
来构建新链表。 -
遍历
list1
和list2
,每次选择较小值连接到current
。 -
最后将剩下未遍历完的链表(如果有)直接连上。
# 最优解:迭代法 + 虚拟头节点 dummy
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def mergeTwoLists(list1: ListNode, list2: ListNode) -> ListNode:
dummy = ListNode(-1)
current = dummy
while list1 and list2:
if list1.val < list2.val:
current.next = list1
list1 = list1.next
else:
current.next = list2
list2 = list2.next
current = current.next
# 连接剩余的节点
current.next = list1 if list1 else list2
return dummy.next
#时间复杂度 O(n + m)
#空间复杂度 O(1)(原地合并)
# 解法2:递归法(更简洁)
def mergeTwoLists(list1: ListNode, list2: ListNode) -> ListNode:
if not list1: #如果任何链表为空,则返回另一个
return list2
if not list2:
return list1
if list1.val < list2.val:
list1.next = mergeTwoLists(list1.next, list2)
return list1
else:
list2.next = mergeTwoLists(list1, list2.next)
return list2
#递归更优雅但栈开销略大
6.总结:链表的核心认知表
特性 | 内容 |
---|---|
操作效率 | 插入/删除 O(1),查找 O(n) |
编程技巧 | 虚拟头节点、快慢指针、递归反转等 |
推荐使用场景 | 插入删除频繁、不能随机访问 |
判断是否使用链表 | 题目涉及节点/引用/链式结构时 |
想要了解更多内容,可在VX小程序搜索🔍AI Pulse,获取更多最新内容。