1. 程式人生 > >【LeetCode題解】160_相交鏈表

【LeetCode題解】160_相交鏈表

結果 user 實現 ini tno href 假設 分享圖片 pytho

目錄

  • 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

復雜度分析同上。

解法二:雙指針(推薦)

思路

雙指針解法顧名思義需要兩個指針,假設指針 pApB 分別指向鏈表 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_相交鏈表