2020年4月6日 星期一

Leetcode題解 Python:Intersection of Two Linked Lists

給兩串 ListNode,回傳 Intersection 的第一個點,如果沒有則回傳 None。

用 memo(hashset) 去紀錄 headA 所有走過的節。再換 headB 走,如果該節在 memo 裡,就是第一個交會點。
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        memo = set()
        PointerA, PointerB = headA, headB
        while PointerA:
            memo.add(PointerA)
            PointerA = PointerA.next
        while PointerB:
            if PointerB in memo: return PointerB
            PointerB = PointerB.next           
        return None
有效歸有效,不過會消耗掉大量記憶體,在一個提示空間複雜度 O(1) 之下,這方法似乎不是最好的。

思考一下,如果要第一個交會,那麼兩者行走距離應該是最短的。但是這樣怎麼寫?

[左1右1]、[左1右2, 左2右1]、....。這樣的安排,太沒有效率了。

我一開始本來想用 traceback,這樣空間複雜度會超出 O(1),就沒有把它寫完了。


看了一下前段班,共同點就是 headA跑完換到headB、headB跑完換到headA。

為什麼可以這樣做?

如果有交會,headA = A part + C part、headB = B part + C part。

兩者一起往下跑,跑完換別的跑,會變成 headA->headB = A part + C part + B part + C part、headB->headA = B part + C part + A part + C part。

A part + C part + B part =  B part + C part + A part,於是兩者將會在 C part 交會。

這是一個循環的技巧應用,如果有想到,就會快很多。
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        PointerA, PointerB = headA, headB
        for _ in range(2):
            while PointerA and PointerB:
                PointerA = PointerA.next
                PointerB = PointerB.next
            if PointerA: PointerB = headA
            else: PointerA = headB
          
        while PointerA and PointerB:
            if PointerA == PointerB: return PointerA
            PointerA = PointerA.next
            PointerB = PointerB.next            
        return None