編寫一個程序,找到兩個單鏈表相交的起始節點。
以下面的兩個鏈表:
在節點 c1 開始相交。php
示例 1:
node
輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 輸出:Reference of the node with value = 8 輸入解釋:相交節點的值爲 8 (注意,若是兩個列表相交則不能爲 0)。 從各自的表頭開始算起,鏈表 A 爲 [4,1,8,4,5],鏈表 B 爲 [5,0,1,8,4,5]。 在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。
示例 2:
面試
輸入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 輸出:Reference of the node with value = 2 輸入解釋:相交節點的值爲 2 (注意,若是兩個列表相交則不能爲 0)。 從各自的表頭開始算起,鏈表 A 爲 [0,9,1,2,4],鏈表 B 爲 [3,2,4]。 在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。
示例 3:
算法
輸入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 輸出:null 輸入解釋:從各自的表頭開始算起,鏈表 A 爲 [2,6,4],鏈表 B 爲 [1,5]。由 於這兩個鏈表不相交,因此 intersectVal 必須爲 0,而 skipA 和 skipB 能夠是任意值。 解釋:這兩個鏈表不相交,所以返回 null。
注意:app
遍歷鏈表 A 並將每一個節點的地址/引用存儲在哈希表中。
而後檢查鏈表 B 中的每個節點是否在哈希表中。若在,則爲相交結點。this
/** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func getIntersectionNode(headA, headB *ListNode) *ListNode { var s [] *ListNode for headA != nil { s = append(s, headA) headA = headA.Next } for headB != nil { for _,v := range(s) { if v == headB { return headB } } headB = headB.Next } return nil }
當鏈表 A 走到尾部的 null 時,轉到鏈表 B 的頭節點繼續走;
當鏈表 B 走到尾部的 null 時,轉到鏈表 A 的頭節點繼續走;
若兩鏈表相交,則 A 和 B 必定相遇。
如上圖:初始化 pA = headA
, pB = headB
,開始遍歷。
pA會先到達鏈表尾,當pA到達末尾時,重置pA爲headB;一樣的,當pB到達末尾時,重置pB爲headA。當pA與pB相遇時,必然就是兩個鏈表的交點。code
爲何要這樣處理?由於對 pA
而言,走過的路程爲 a+c+b
,對 pB
而言,爲 b+c+a
,顯然 a+c+b = b+c+a
,這就是該算法的核心原理。blog
即便兩個鏈表沒有相交點,仍然能夠統一處理,由於這種狀況意味着相交點就是 null
,也就是上圖中的公共部分c沒有了,從而遞推式變成了 pA: a+b
,pB: b+a
,一樣是成立的。ip
時間複雜度:O(m+n),空間複雜度:O(1)。內存
/** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func getIntersectionNode(headA, headB *ListNode) *ListNode { if (headA == nil || headB == nil) { return nil } pA, pB := headA, headB for pA != pB { if pA == nil { pA = headB } else { pA = pA.Next } if pB == nil { pB = headA } else { pB = pB.Next } } return pA }
兩個單鏈表,有公共結點,則必然尾部公用;
分別找出鏈表 1 和鏈表 2 的長度,長的鏈表減去短的鏈表得出一個 n 值;
長的鏈表先走 n 步,兩個鏈表再同時移動,則兩個鏈表相交點就是第一個公共結點。
時間複雜度:O(n),空間複雜度:O(1)。
/** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func getIntersectionNode(headA, headB *ListNode) *ListNode { if headA == nil || headB == nil { return nil } lenA := getLen(headA) lenB := getLen(headB) //計算鏈表長度差 n,長的先移動 n 步 if (lenA > lenB) {// 鏈表A比鏈表B長,A先移動 for i := 0; i < lenA - lenB; i++ { headA = headA.Next } } else {// 鏈表B比鏈表A長,B先移動 for i := 0; i < lenB - lenA; i++ { headB = headB.Next } } for headA != nil { if headA == headB { return headA } headA = headA.Next headB = headB.Next } return nil; } //獲取鏈表長度 func getLen(head *ListNode) int { var len int for head != nil { len++ head = head.Next } return len }
這道題也是面試題中常常遇到的「兩個鏈表的第一個公共節點」。雖然難度是「簡單」,但對我來講一點也不簡單。看評論和題解漲了不少姿式。
並且,一開始用的是 PHP,可怎麼都通不過,不得不用 Go 重寫了一遍,卻是很快經過了。
附上 PHP 版本:
class ListNode { public $val = 0; public $next = null; function __construct($val) { $this->val = $val; } } /** * @param ListNode $headA * @param ListNode $headB * @return ListNode */ function getIntersectionNode($headA, $headB) { $hashMap = []; while ($headA) { $hashMap[] = $headA; $headA = $headA->next; } while ($headB) { if (in_array($headB, $hashMap)) { return $headB; } $headB = $headB->next; } return null; }
function getIntersectionNode($headA, $headB) { if ($headA == null || $headB == null) { return null; } $pA = $headA; $pB = $headB; while ($pA != $pB) { $pA = $pA == null ? $headB : $pA->next; $pB = $pB == null ? $headA : $pB->next; } return $pA; }
function getIntersectionNode($headA, $headB) { $lenA = getListLength($headA); $lenB = getListLength($headB); $diffLen = $lenA > $lenB ? $lenA - $lenB : $lenB - $lenA; $headLong = $lenA > $lenB ? $headA : $headB; $headShort = $lenA > $lenB ? $headB : $headA; //先在長鏈表上走幾步,再同時在兩個鏈表上遍歷 for ($i = 0; $i < $diffLen; $i++) { $headLong = $headLong->next; } while ($headLong != $headShort) { $headLong = $headLong->next; $headShort = $headShort->next; } return $headLong; } /** * 獲取鏈表的長度 */ function getListLength($head) { $length = 0; $current = $head; while ($current != null) { $length++; $current = $current->next; } return $length; }