給定兩個單鏈表頭節點 headA、headB,找到它們的相交起始節點。相交定義為節點引用相同 (非值相等),即兩鏈表共享同一尾部節點序列。本文對比三種解法,重點分析雙指針 O(1) 空間巧解。
List A: a1 → a2 → c1 → c2 → c3 List B: b1 → b2 → b3 → c1 → c2 → c3 相交節點: c1 (引用相同) 無相交: 返回 null
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set<ListNode> visited = new HashSet<>();
// 遍歷 A,記錄所有節點引用
ListNode curr = headA;
while (curr != null) {
visited.add(curr);
curr = curr.next;
}
// 遍歷 B,首個在 Set 中的即為相交點
curr = headB;
while (curr != null) {
if (visited.contains(curr)) {
return curr; // 找到相交節點
}
curr = curr.next;
}
return null; // 無相交
}
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode pA = headA, pB = headB;
// 關鍵:兩指針走過的總路徑長度相等 = lenA + lenB
// 若有相交:在相交點相遇
// 若無相交:同時到達 null 退出
while (pA != pB) {
pA = (pA == null) ? headB : pA.next; // 走完 A 轉走 B
pB = (pB == null) ? headA : pB.next; // 走完 B 轉走 A
}
return pA; // 相交節點或 null
}
設:
abc指針 pA 走過路徑:a + c + b
指針 pB 走過路徑:b + c + a
總長度相等,故若有相交 (c>0),必在相交點相遇;若無相交 (c=0),同時走完 a+b 到達 null。
A: a1 → a2 → c1 → c2 → c3 → null
↑
B: b1 → b2 → b3 ──────┘
pA 路徑: a1 a2 c1 c2 c3 b1 b2 b3 c1 ← 相遇
pB 路徑: b1 b2 b3 c1 c2 c3 a1 a2 c1 ← 相遇
↑ ↑
切換到 headB 切換到 headA
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA = getLength(headA);
int lenB = getLength(headB);
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;
}
// 同步前進
while (pA != pB) {
pA = pA.next;
pB = pB.next;
}
return pA;
}
private int getLength(ListNode head) {
int len = 0;
while (head != null) { len++; head = head.next; }
return len;
}
| 解法 | 時間 | 空間 | 優點 | 缺點 |
|---|---|---|---|---|
| HashSet | O(m+n) | O(m) | 直觀易懂、易實現 | 額外空間 |
| 雙指針路徑對齊 | O(m+n) | O(1) | 空間最優、單遍掃描、代碼極簡 | 思維難度高、需數學證明 |
| 先測長度對齊 | O(m+n) | O(1) | 邏輯清晰、易證明 | 需兩遍掃描 |