将用“寻宝猎人”的故事贯穿5大高频链表算法题,结合Java代码和复杂度分析,带你轻松掌握解题精髓!每个解法都附带记忆口诀和实战技巧,助你面试游刃有余。
🔍 故事背景:寻宝猎人的链式冒险
你是一名寻宝猎人,手中有一张神秘藏宝图(链表)。每个藏宝点(节点)标注着宝藏价值(val)和下一个藏宝点线索(next)。你需要完成以下任务:
- 翻转路线(反转链表)
- 识破循环陷阱(环形链表检测)
- 合并藏宝图(合并有序链表)
- 精准拆除炸弹(删除倒数第N节点)
- 寻找交汇点(链表相交)
🔄 一、翻转藏宝路线(反转链表)
剧情:藏宝图顺序被加密,需从尾到头重新连接线索。
口诀:“三指针舞步,prev-cur-next 轮转”
1. 迭代法(O(n)时间,O(1)空间)
java
Copy
// 三指针翻转:prev记录前驱,cur处理当前,next暂存后继[5](@ref)
ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next; // 1. 暂存下一站线索
cur.next = prev; // 2. 当前节点指向前驱(翻转箭头)
prev = cur; // 3. 前驱指针前进
cur = next; // 4. 当前指针前进
}
return prev; // 新头节点是原链表末尾
}
关键点:
- 每次翻转一个节点的指针方向
- 结束时
prev指向新头部
2. 递归法(O(n)时间,O(n)栈空间)
java
Copy
ListNode reverseRecursive(ListNode head) {
if (head == null || head.next == null) return head;
ListNode newHead = reverseRecursive(head.next); // 递归到尾部
head.next.next = head; // 后一节点指回当前节点
head.next = null; // 当前节点断开原指针
return newHead;
}
💡 适用场景:面试官要求多种解法时展示递归思维,但迭代法更优
⭕ 二、识破循环陷阱(环形链表检测)
剧情:藏宝图有循环路线,陷入则永无尽头,需快速检测。
口诀:“龟兔赛跑,相遇即环”
弗洛伊德快慢指针法(O(n)时间,O(1)空间)
java
Copy
boolean hasCycle(ListNode head) {
if (head == null) return false;
ListNode slow = head; // 乌龟每次走1步
ListNode fast = head; // 兔子每次走2步
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true; // 相遇说明有环
}
return false;
}
数学证明:
- 设环前长度
L,环长C - 相遇时慢指针走
L + D,快指针走L + D + kC - 快指针速度是慢指针2倍 →
2(L + D) = L + D + kC→L = kC - D
进阶:找环入口点?相遇后重置
slow=head,slow和fast同速走L步即入口
🧩 三、合并藏宝图(合并两个有序链表)
剧情:两份藏宝图(升序排列),需合并成一张完整图。
口诀:“哑节点护头,谁小连谁走”
迭代法(O(n+m)时间,O(1)空间)
java
Copy
ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1); // 虚拟头节点(避免空指针)
ListNode cur = dummy;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
cur.next = l1; // 连接较小节点
l1 = l1.next;
} else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next; // 新链指针前进
}
cur.next = (l1 != null) ? l1 : l2; // 接上剩余部分
return dummy.next; // 返回真实头节点
}
关键技巧:
-
虚拟头节点(
dummy)简化边界处理 -
无需额外空间,直接复用原节点
💣 四、精准拆除炸弹(删除倒数第N节点)
剧情:倒数第N个藏宝点有炸弹,需精准拆除且不触发警报。
口诀:“快指针先走N步,同步移动慢指针”
快慢指针法(O(n)时间,O(1)空间)
java
Copy
ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1, head); // 虚拟头防删头节点
ListNode fast = dummy, slow = dummy;
// 快指针先走N+1步(让slow停在目标前驱)
for (int i = 0; i <= n; i++) fast = fast.next;
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next; // 跳过目标节点
return dummy.next;
}
为什么是N+1步?
-
让
slow停在待删节点的前驱位置 -
例:删除倒数第2个节点(链表
1→2→3→4)Copy 初始:dummy→1→2→3→4 fast先走3步:到3 slow最终停在2(前驱),删除3:2→4
❤️ 五、寻找交汇点(链表相交)
剧情:两份藏宝图在某点后重合,需找到第一个交汇藏宝点。
口诀:“算长度差,齐头并进找交点”
长度差+双指针法(O(n+m)时间,O(1)空间)
java
Copy
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 1. 计算两链长度
int lenA = getLength(headA), lenB = getLength(headB);
// 2. 长链指针先走 |lenA-lenB| 步
ListNode pA = headA, pB = headB;
if (lenA > lenB) {
for (int i = 0; i < lenA - lenB; i++) pA = pA.next;
} else {
for (int i = 0; i < lenB - lenA; i++) pB = pB.next;
}
// 3. 双指针齐头并进找交点
while (pA != pB) {
pA = pA.next;
pB = pB.next;
}
return pA; // 相交点或null
}
int getLength(ListNode head) {
int len = 0;
while (head != null) {
len++;
head = head.next;
}
return len;
}
数学原理:
-
两链相交后长度相同 → 对齐起点后同步遍历必相遇
-
若不相遇则
pA和pB同时走到null退出
💎 高频链表算法思维导图
⚠️ 避坑指南(面试致命陷阱)
-
空指针异常:
- 总是检查
head == null - 访问
next前确认当前节点非空
- 总是检查
-
指针丢失:
- 翻转链表时先存
next = cur.next再改指针
- 翻转链表时先存
-
环检测误区:
- 快指针需判断
fast != null && fast.next != null
- 快指针需判断
最后挑战:尝试用快慢指针解决 “判断回文链表”(先找中点,再翻转后半段比较)。掌握这5题,90%链表面试题迎刃而解!