1. 程式人生 > >【leetcode】Python實現-141.環形連結串列

【leetcode】Python實現-141.環形連結串列

141.環形連結串列

描述

給定一個連結串列,判斷連結串列中是否有環。
進階:
你能否不使用額外空間解決此題?

我…遍歷了以後超出時間限制,於是看大家總結的方法。一個就是設定兩個指標slow和fast,一個步長為1,一個步長為2進行遍歷。如果有環,則slow和fast總會在某一點相遇。如果沒有環,則fast會先為空,或者fast.next為空。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
# self.next = None class Solution(object): def hasCycle(self, head): """ :type head: ListNode :rtype: bool """ if head is None or head.next is None or head.next.next is None: return False slow = head.next fast = head.next.next while
slow != fast and fast is not None and fast.next is not None: slow = slow.next fast = fast.next.next if fast == slow: return True else: return False

通過了呢,但是我覺得我的程式碼不夠優雅,還可以再進行簡化。
關於環形連結串列的相關問題可以檢視

class Solution(object):
    def hasCycle
(self, head):
""" :type head: ListNode :rtype: bool """ fast = slow = head while fast and fast.next: fast = fast.next.next slow = slow.next if slow == fast: return True return False

關於環形連結串列問題的總結(from Rotten_Pencil)
1.快慢指標一直到相遇時的迴圈次數等於環的長度。(可推導)
Case1:一個完美的環狀連結串列,即連結串列頭尾相連

一個環形連結串列:{A,B,C,A,B,C,……}
其上存在兩個指標,A指標移動速度是B指標的兩倍。
A,B同時從節點1出發,所經過的節點如下:
快指標A:A->C->B->A
慢指標B:A->B->C->A
A、B指標在節點A第一次相遇,迴圈次數為3,而環的程度正好也為3。那這個是不是巧合呢?
首先我們要理解的是迴圈的次數代表的是什麼。
1. 每次迴圈,對於B這個慢指標來說,意味著走了一個單位長度。
2. 而對於A來說,走了兩個單位長度。
3. 那麼二者第一次相遇必然是在A走了2圈,B走了1圈的時候。
4. 假如A的速度是B的3倍,那麼二者第一次相遇是在A走了3圈,B走了1圈的時候。
5. 同理A是B的5倍速度,相遇時A走了5圈,B走了1圈

n. A的速度是B的n倍,相遇時A走了n圈,B走了1圈
從上面的觀察我們可以發現,無論A的速度是B的幾倍,兩者第一次相遇必然是在B走了1圈時。
因為B的速度代表的是連結串列基本的長度單位,即從一個節點移動到下一個節點的距離。
同時在連結串列中,每個節點與節點之間這個距離是不變的。
當迴圈結束時,B走了1圈,正好是環的長度。而B每次移動一個單位距離,因此環的長度等於迴圈次數。

Case2:不完美的環狀連結串列,即,連結串列中某一中間節點與尾部相連
不完美的環形連結串列

一個環形連結串列(如圖所示):{D,E,A,B,C,A,B,C,……}
其上存在兩個指標,A指標移動速度是B指標的兩倍。
A,B同時從節點1出發,所經過的節點如下:
快指標A:D->A->C->B
慢指標B:D->E->A->B
根據上圖,我們可以計算出A、B行走的距離:
A = d+e+a+b+c+a
B = d+e+a
因為A的速度是B的2倍,那麼A行走的距離也因該是B的2倍:
d+e+a+b+c+a = 2(d+e+a)
a+b+c = d+e+a
從上圖可以看出,a+b+c正好是環的長度,而d+e+a則是B行進的距離。
又知,每次迴圈B移動一個單位距離,因此在不完美的環狀表中,迴圈次數亦是等於環的長度。

2.快慢指標相遇點到環入口的距離 = 連結串列起始點到環入口的距離。(可推導)

根據上文公式,我們可以繼續推導,即:
a+b+c = d+e+a
b+c = d+e
b+c是相遇點到環入口的距離
d+e是連結串列起點到環入口的距離

相關問題
- 判斷是否為環形連結串列
思路:使用追趕的方法,設定兩個指標slow、fast,從頭指標開始,每次分別前進1步、2步。如存在環,則兩者相遇;如不存在環,fast遇到NULL退出。
- 若為環形連結串列,求環入口點
思路:快慢指標相遇點到環入口的距離 = 連結串列起始點到環入口的距離
- 求環的長度
思路:記錄下相遇點p,slow、fast從該點開始,再次碰撞所走過的運算元就是環的長度s
- 判斷兩個連結串列是不是相交(思路:如果兩個連結串列相交,那麼這兩個連結串列的尾節點一定相同。直接判斷尾節點是否相同即可。這裡把這道題放在環形連結串列,因為環形連結串列可以拆成Y字的兩個連結串列。)

142.環形連結串列 II

描述(雖然這是中等難度的題,不過我覺得有必要跟上一題放在一起。我可以考慮按型別來做題,不再按難度來做題了):
>
給定一個連結串列,返回連結串列開始入環的第一個節點。 如果連結串列無環,則返回 null。
說明:不允許修改給定的連結串列。
進階:
你是否可以不用額外空間解決此題?

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head is None or head.next is None:
            return None
        fast = head
        slow = head
        while fast.next and fast.next.next:
            slow = slow.next
            i+=1
            fast = fast.next.next
            if slow == fast:
                p = head
                while slow != p:
                    p = p.next
                    slow = slow.next
                return p
        return None

思路就是上面總結的:相遇點到環入口點的距離=頭節點到環入口點的距離