環形鏈表進階版【手繪漫畫】面試必考之雙指針(LeetCode 142)

在這裏插入圖片描述

圖解算法與數據結構

1、前言

今天開始的是雙指針!

下面一起來看看吧!!!
在這裏插入圖片描述
讓我們從一個經典問題開始:

給定一個鏈表,判斷鏈表中是否有環。

你可能已經使用 哈希表 提出瞭解決方案。但是,使用 雙指針 技巧有一個更有效的解決方案。

想象一下,有兩個速度不同的跑步者。如果他們在直路上行駛,快跑者將首先到達目的地。但是,如果它們在圓形跑道上跑步,那麼快跑者如果繼續跑步就會追上慢跑者。

這正是我們在鏈表中使用兩個速度不同的指針時會遇到的情況:

  1. 如果沒有環,快指針將停在鏈表的末尾。
  2. 如果有環,快指針最終將與慢指針相遇。

所以剩下的問題是:

這兩個指針的適當速度應該是多少?

一個安全的選擇是每次移動慢指針一步,而移動快指針兩步。每一次迭代,快速指針將額外移動一步。如果環的長度爲 M,經過 M 次迭代後,快指針肯定會多繞環一週,並趕上慢指針。

爲了讓你更懂,下面來看一個題吧!
在這裏插入圖片描述

2、實例

LeetCode 707,一個設計鏈表的題。

在這裏插入圖片描述

3、正文

一起來看一下:

  1. 雙指針初始化:

    1. 設雙指針 fastslow 指向鏈表頭部 head
    2. fast 每輪走 1 步;
    3. slow 每輪走 1 步;
  2. 兩種情況

    1. 第一種情況:不出意外,fast 每輪再多走 1 步(這纔是名副其實的快指針~);
      在這裏插入圖片描述
    2. 第二種情況fast 走到鏈表末端,下一節點爲空,說明鏈表無環,直接 break,返回 NULL(如果存在環,兩個指針必然會相遇,追擊問題,fast 速度是 slow 的二倍~);
      在這裏插入圖片描述
  3. 雙指針第一次相遇(當 fast == slow 時)

    1. 設鏈表共有 a+b 個節點,其中 鏈表頭到環的入口a 個節點(不算環入口節點),b 個節點,若兩指針分別走了 fs 步(fast,slow 的英文縮寫);
    2. 路程等於速度乘以時間,fast 的速度是 slow 的 2 倍,所以 fast 走的步數是 slow 的 2 倍,即 f = 2*s ①;
    3. 雙指針最後都是在環內繞圈直到重合,所以 fastslow 多走了 n 個環的長度,即 f = s + n*b ②;
    4. 以上兩式 ① ② 相減得:f = 2n*bs = n*b,即 fastslow 分別走了 2nn
      在這裏插入圖片描述
  4. 雙指針第二次相遇(當 fast == slow 時)

    1. slow 指針不動,fast 重新指向鏈表頭節點(f = 0,s = n*b);
    2. slowfast 同時每輪向前走 1 步;
    3. fastf = a 步時,slows = a+nb 步,此時 兩指針重合,並同時指向環入口b 是環的長度!整數倍 b 就是環入口)。
      在這裏插入圖片描述
  5. 返回 fast 指針指向的節點(slow 也行,因爲終止條件是 fast == slow)。


這個位置有個非常靈性的操作,即:
在這裏插入圖片描述
分析分析

:如何才能恰好在環入口節點相遇呢?

:如果走過的路程滿足 a+n*bn 可以是任意自然數值(0,1,2…),先走 a 步到環入口節點,之後無論繞多少圈環(n*b 步)都會再次回到入口節點,即相遇了~~~~而現在這個時間點,slow 走過的步數是 nb,只要想辦法再走 a 步停下來,就可以到環的入口,同時 fast 也要走 a 步,這樣兩者就相遇了!所以依然是雙指針法,fast 重新初始化爲鏈表頭 head,這樣走 a 步就到了環入口,而 slow 也走 a 步,變成了 a+n*b,到達了環入口節點。

妙啊!!!
在這裏插入圖片描述
在這裏插入圖片描述

4、代碼

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        auto fast=head,slow=head;
        while(fast){
            fast=fast->next;
            slow=slow->next;
            if(fast) fast=fast->next;
            else break;

            if(fast==slow){
                fast=head;
                while(fast!=slow){
                    fast=fast->next;
                    slow=slow->next;
                }
                return fast;
            } 
        }
        return NULL;
    }
};

在這裏插入圖片描述
在這裏插入圖片描述

如果有幸幫到你,請幫我點個【贊】,給個【關注】!如果能順帶【評論】給個鼓勵,我將不勝感激。

如果想要更多的資源,歡迎關注 @我是管小亮,文字強迫症MAX~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章