【LeetCode題解】160_相交鏈表
目錄
- 160_相交鏈表
- 描述
- 解法一:哈希表
- 思路
- Java 實現
- Python 實現
- 解法二:雙指針(推薦)
- 思路
- Java 實現
- Python 實現
160_相交鏈表
描述
編寫一個程序,找到兩個單鏈表相交的起始節點。
例如,下面的兩個鏈表:
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
在節點 c1 開始相交。
註意:
- 如果兩個鏈表沒有交點,返回
null
. - 在返回結果後,兩個鏈表仍須保持原有的結構。
- 可假定整個鏈表結構中沒有循環。
- 程序盡量滿足 O(n) 時間復雜度,且僅用 O(1) 內存。
致謝:
特別感謝 @stellari 添加此問題並創建所有測試用例。
解法一:哈希表
思路
首先遍歷鏈表 A 的所有節點,並將每個節點的引用存入哈希表中。接著,遍歷鏈表 B 的每個節點,如果某個節點的引用在哈希表中存在,返回該節點的引用;如果遍歷完鏈表 B 的所有節點沒有發現這樣一個節點,則返回 null
。
Java 實現
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { if (headA == null || headB == null) { return null; } Set<ListNode> nodes = new HashSet<>(); ListNode temp = headA; while (temp != null) { nodes.add(temp); temp = temp.next; } temp = headB; while (temp != null) { if (nodes.contains(temp)) { return temp; } temp = temp.next; } return null; } }
復雜度分析:
- 時間復雜度:\(O(m+n)\),其中,\(m\) 表示鏈表 A 的節點數,\(n\) 表示鏈表 B 的節點數,最壞的情況下,需要遍歷兩個鏈表的所有節點
- 空間復雜度:\(O(m)\) 或者 \(O(n)\)
Python 實現
# Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution(object): def getIntersectionNode(self, headA, headB): """ :type head1, head1: ListNode :rtype: ListNode """ if not headA or not headB: return None nodesA = set() curr = headA while curr: nodesA.add(curr) curr = curr.next curr = headB while curr: if curr in nodesA: return curr curr = curr.next return None
復雜度分析同上。
解法二:雙指針(推薦)
思路
雙指針解法顧名思義需要兩個指針,假設指針 pA
和 pB
分別指向鏈表 A 和鏈表 B 的頭結點,之後兩個指針分別以步幅為 1 的速度向鏈表的尾部遍歷,當指針 pA
遍歷到鏈表 A 的尾節點時,將指針 pA
指向鏈表 B 的頭部。同樣地,當指針 pB
遍歷到鏈表 B 的尾節點時,將指針 pB
指向鏈表 A 的頭部。當兩個指針相遇時,指針 pA
或者 pB
所指向的節點就是兩個鏈表的相交節點。
為了說明雙指針的求解思路,假設鏈表 A 和鏈表 B 的結構如下圖所示,
其中,鏈表 A 包含 6 個節點,節點的值分別為 1、3、5、7、9 和 11;鏈表 B 包含 4 個節點,節點的值分別為 2、4、9 和 11,因此,兩個鏈表的相交節點為 9。設鏈表 A 中不相交的部分(即藍色部分的節點)長度為 \(L1\),鏈表 B 中不相交的部分(即黃色部分的節點)長度為 \(L2\),兩個鏈表相交的部分(即紅色部分的節點)長度為 \(L3\)。
如下圖所示,當指針 pB
遍歷到鏈表 B 的尾節點 11 時,指針 pA
遍歷到鏈表 A 中節點 7 的位置,下一次遍歷指針 pB
將處於鏈表 A 的節點 1 的位置。
同理,當指針 pA
遍歷到鏈表 A 的尾節點 11 時,此時指針 pB
處於鏈表 A 中節點 3 的位置,下一次遍歷指針 pA
將處於鏈表 B 的節點 2 位置。
再經過兩次遍歷後,指針 pA
將位於鏈表 B 中節點 4 的位置,而指針 pB
也將到達鏈表 A 的節點 4 的位置,下一次遍歷兩個指針將在節點 9(即相交節點)相遇。此時,兩個指針走過的長度都為 \(L1 + L2 + L3\)。究其原因,可以將兩個指針走過的“路程”看成 3 個部分,即藍色部分、紅色部分以及橙色部分,只是兩個指針走過 3 個部分的順序是不同的,指針 pA
先走藍色部分而指針 pB
先走橙色部分,但是經過前 3 個部分後,兩個指針走過的長度一定是相同的,因此在下一次遍歷的時候兩個指針一定會相遇。
Java 實現
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA;
ListNode pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
復雜度分析:
- 時間復雜度:\(O(L1 + L2 + L3) = O(n)\),如果兩個鏈表存在相交節點,則經過 \(L1 + L2 + L3\) 的“長度”後,兩個指針一定會相遇
- 空間復雜度:\(O(1)\),只需要保存兩個引用
Python 實現
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
if not headA or not headB:
return None
p_a, p_b = headA, headB
while p_a != p_b:
if p_a:
p_a = p_a.next
else:
p_a = headB
if p_b:
p_b = p_b.next
else:
p_b = headA
return p_a
復雜度分析同上。
【LeetCode題解】160_相交鏈表