找到兩個單鏈表相交的起始節點。本文詳解「先求長度、長鏈表先走差值步、雙指針同步前進」的 O(n) 時間 O(1) 空間標準解法,並提供無需求長度的雙指針巧法(雙倍遍歷)作為進階拓展。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *pA = headA, *pB = headB;
int lenA = 0, lenB = 0;
// 1. 計算長度
while (pA) { lenA++; pA = pA->next; }
while (pB) { lenB++; pB = pB->next; }
// 2. 重置指針
pA = headA; pB = headB;
// 3. 長鏈表先走差值步
if (lenA > lenB) {
int diff = lenA - lenB;
while (diff--) pA = pA->next;
} else {
int diff = lenB - lenA;
while (diff--) pB = pB->next;
}
// 4. 同步前進尋找相交
while (pA && pB) {
if (pA == pB) return pA; // 找到相交節點
pA = pA->next;
pB = pB->next;
}
return NULL; // 無相交
}
**核心思想**:指針 `pA` 走完 `A` 再走 `B`,`pB` 走完 `B` 再走 `A`。若相交,兩指針走過總長度相同(`lenA + lenB - lenCommon`),必在相交點相遇;若不相交,同為 `NULL` 相遇。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *pA = headA, *pB = headB;
if (!pA || !pB) return NULL;
while (pA != pB) {
pA = pA ? pA->next : headB; // 走完A轉入B
pB = pB ? pB->next : headA; // 走完B轉入A
}
return pA; // 相交節點或NULL
}
| 維度 | 長度對齊法 | 雙倍遍歷法 |
|---|---|---|
| 時間複雜度 | O(m+n) 兩次遍歷 | O(m+n) 至多兩次遍歷 |
| 空間複雜度 | O(1) | O(1) |
| 代碼長度 | 較長 | 極短(6 行核心邏輯) |
| 易理解性 | 直觀,教科書標準解 | 需「頓悟」對稱性 |
| 面試推薦 | 先寫此法,再亮出優雅解 | 加分項,展示數學直覺 |
// 相交情況
A: 1→2→3
↘
6→7→NULL
↗
B: 4→5
相交節點:6
// 無相交
A: 1→2→3→NULL
B: 4→5→NULL
返回:NULL
// 一鏈表為空
A: NULL
B: 1→2→NULL
返回:NULL
相交鏈表問題通過**長度對齊**或**對稱雙倍遍歷**完美解決。面試建議:先寫長度對齊法展示扎實基本功,再補充雙倍遍歷法體現進階思維。理解「指針地址相等即相交」是核心,切勿混淆為值相等。