天天看點

【環形連結清單】兩種主流題目套路化模闆總結,附思路解析。

文章目錄

  • ​​判斷是否有環​​
  • ​​找到環的入口​​

環形連結清單,就像下面這樣的連結清單。

【環形連結清單】兩種主流題目套路化模闆總結,附思路解析。

環形連結清單有兩種主流題目,一種是判斷該連結清單是否有環,另一種就是找到環形連結清單的環入口。

這兩種題要是沒有一定的方法,判斷起來會非常亂,是以總結一下方法。

判斷是否有環

使用快慢指針法,分别定義 fast 和 slow 指針,從頭結點出發,fast指針每次移動兩個節點,slow指針每次移動一個節點,如果 fast 和 slow指針在途中相遇 ,說明這個連結清單有環。

為什麼快慢指針一定會在環内相遇呢?

我一開始也是這個問題,為什麼他們不會錯開呢,其實這個問題畫一個連結清單走一下就知道了,fast指針一次走兩個,slow一次走一個,也就是他們兩個相對的位移是一個機關,即fast指針每次以一個機關的距離靠近slow,是以不會錯開。

對應題目: ​​leetcode 141. 環形連結清單​​

完整代碼:

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        # 卡哥一刷
        fast = head
        slow = head
        while fast!=None and fast.next !=None:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                # 兩者在環内相遇了 
                return True # 有環
        return False # 沒有環      

找到環的入口

這種題的思路其實比較麻煩,要不是我看了卡哥的推導,估計會在這裡卡上一陣。

設一個環形連結清單如下圖所示。

【環形連結清單】兩種主流題目套路化模闆總結,附思路解析。

先放結論:

從頭結點出發一個指針,從相遇節點 也出發一個指針,這兩個指針每次隻走一個節點, 那麼當這兩個指針相遇的時候就是 環形入口的節點。

證明過程我覺得沒幾個人想看,雖然很簡單,但是也懶得看,是以我放最後了。

對應題目:​​leetcode 142. 環形連結清單 II​​

完整代碼

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

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        # 卡哥一刷
        fast = head
        slow = head
        while fast!=None and fast.next !=None:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                # 兩者在環内相遇了 記錄此時下标值
                index1 = fast
                index_start = head # 記錄開始點
                while index1 != index_start:
                    # 兩者同時移動,在相交的時候就是環的入口
                    index1 = index1.next
                    index_start = index_start.next
                return index1
        return None      

證明過程:

slow指針走過的節點數為: x + y, fast指針走過的節點數:x + y + n (y + z),n為fast指針在環内走了n圈才遇到slow指針, (y+z)為 一圈内節點的個數A。

因為fast指針是一步走兩個節點,slow指針一步走一個節點, 是以 fast指針走過的節點數 = slow指針走過的節點數 * 2:

(x + y) * 2 = x + y + n (y + z)

兩邊消掉一個(x+y): x + y = n (y + z)

因為要找環形的入口,那麼要求的是x,因為x表示 頭結點到 環形入口節點的的距離。

是以要求x ,将x單獨放在左面:x = n (y + z) - y ,

再從n(y+z)中提出一個 (y+z)來,整理公式之後為如下公式:x = (n - 1) (y + z) + z 注意這裡n一定是大于等于1的,因為 fast指針至少要多走一圈才能相遇slow指針。

拿n為1的情況來舉例,意味着fast指針在環形裡轉了一圈之後,就遇到了 slow指針了。

當 n為1的時候,公式就化解為 x = z。

那麼 n如果大于1是什麼情況呢,就是fast指針在環形轉n圈之後才遇到 slow指針。

其實這種情況和n為1的時候 效果是一樣的,一樣可以通過這個方法找到 環形的入口節點,隻不過,index1 指針在環裡 多轉了(n-1)圈,然後再遇到index2,相遇點依然是環形的入口節點。