LeetCode 热题 100:链表专题解析
本文将详细讲解两道经典的 LeetCode 链表题目:
- 160. 相交链表
- 206. 反转链表
我们将从题目分析、解题思路、代码实现到复杂度分析进行全面解析,帮助你深入理解链表操作的核心技巧。
🧩 题目一:160. 相交链表
🔍 题目描述
给你两个单链表的头节点 headA 和 headB,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null。
注意:保证整个链式结构中不存在环,且函数返回后链表结构必须保持不变。
📌 示例图示
A: a1 → a2 → c1 → c2 → c3
↘
B: b1 → b2 → b3 → c1 → c2 → c3
相交于节点 c1。
💡 解题思路
方法一:双指针法(推荐)
核心思想:
让两个指针分别遍历两个链表,当一个指针到达末尾时,跳转到另一个链表的头部继续遍历。这样可以确保两个指针走过的总长度相同。
假设:
- 链表 A 的非公共部分长度为
a- 链表 B 的非公共部分长度为
b- 公共部分长度为
c
那么:
- 指针 A 走了
a + c + b步 - 指针 B 走了
b + c + a步
两者最终会同时到达交点或同时为 null。
✅ 优势:
- 时间复杂度 O(m+n)
- 空间复杂度 O(1)
- 不需要额外存储空间
✅ JavaScript 实现
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
var getIntersectionNode = function(headA, headB) {
let pA = headA;
let pB = headB;
while (pA !== pB) {
pA = pA === null ? headB : pA.next;
pB = pB === null ? headA : pB.next;
}
return pA; // 返回相交节点,若无则为 null
};
📊 复杂度分析
| 项目 | 分析 |
|---|---|
| 时间复杂度 | O(m + n),m 和 n 分别是两个链表的长度 |
| 空间复杂度 | O(1),仅使用常量级额外空间 |
🧠 小贴士
- 这种“双指针交替遍历”的技巧在处理链表长度不一致问题时非常有效。
- 若链表有环,需先判断是否有环再求交点(但本题已说明无环)。
🧩 题目二:206. 反转链表
🔍 题目描述
给你单链表的头节点 head,请你反转链表,并返回反转后的链表。
📌 示例
输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]
💡 解题思路
方法一:迭代法(最常用)
核心思想:
逐个修改每个节点的 next 指针,使其指向前一个节点。
我们用三个变量:
prev:前一个节点curr:当前节点nextTemp:保存下一个节点(防止丢失)
每一步操作如下:
- 保存
curr.next - 将
curr.next指向prev - 移动
prev和curr
🔄 流程示意
1 → 2 → 3 → null
prev = null, curr = 1
nextTemp = 2
curr.next = prev → 1 → null
prev = 1, curr = 2
...
最终结果:5 → 4 → 3 → 2 → 1 → null
✅ JavaScript 实现
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
let prev = null;
let curr = head;
while (curr !== null) {
let nextTemp = curr.next; // 保存下一个节点
curr.next = prev; // 反转指针
prev = curr; // 移动 prev
curr = nextTemp; // 移动 curr
}
return prev; // prev 是新的头节点
};
📊 复杂度分析
| 项目 | 分析 |
|---|---|
| 时间复杂度 | O(n),n 是链表长度 |
| 空间复杂度 | O(1),只用了常量级额外空间 |
🧠 小贴士
- 初学者容易忘记保存
next节点,导致链表断裂。 - 可以画图辅助理解指针变化过程。
- 递归方法虽然简洁,但空间复杂度为 O(n),不如迭代高效。
🎯 总结对比
| 题号 | 名称 | 核心技巧 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|---|
| 160 | 相交链表 | 双指针交替遍历 | O(m+n) | O(1) |
| 206 | 反转链表 | 迭代指针翻转 | O(n) | O(1) |
✅ 学习建议
-
动手画图:链表题一定要画出指针移动过程,避免逻辑混乱。
-
边界情况:注意空链表、单节点等特殊情况。
-
多语言练习:尝试用 Python、Java、C++ 再写一遍,加深理解。
-
拓展思考:
- 如何判断链表是否有环?
- 如何在 O(1) 空间内合并两个有序链表?
🚀 推荐进阶题目
-
- 环形链表
-
- 环形链表 II
-
- 合并两个有序链表
-
- 回文链表
通过掌握这两道基础链表题,你可以打下坚实的数据结构基础,为后续更复杂的算法题做好准备!
📌 坚持每日一题,轻松拿下 LeetCode!