【刷題】Linked List Cycle II

原題戳我

題目

Description

Given a linked list, return the node where the cycle begins.

If there is no cycle, return null.

Example

Given -21->10->4->5, tail connects to node index 1,return 10

Challenge

Follow up:

Can you solve it without using extra space?

分析

問題分解

basic problem

單鏈表中確定有無環(huán)

暴力方法是使用hash表統(tǒng)計(jì)節(jié)點(diǎn)的出現(xiàn)次數(shù),O(n)的space。不過一般是希望以O(shè)(1)的space解決:

  1. 設(shè)置一個(gè)快指針fast和一個(gè)慢指針slow,它們同時(shí)從鏈表頭開始往后遍歷,快指針每次移動(dòng)兩個(gè)位置,慢指針每次移動(dòng)一個(gè)位置
  2. 如果在快指針訪問到null(即無環(huán))之前,快指針和慢指針相遇,就說明有環(huán)

follow up

確定環(huán)入口入口的位置

和basic problem一樣,可以用hash表解決。而follow up的目的仍然是希望O(1)的space解決:

  1. 重復(fù)上述判定有環(huán)無環(huán)的過程
  2. 用一個(gè)新指針指向head,與slow指針同時(shí)一次移動(dòng)一個(gè)位置
  3. 當(dāng)head與slow相遇時(shí),指針?biāo)傅墓?jié)點(diǎn)即入口節(jié)點(diǎn)

證明

相遇一定有環(huán)

反證法:

如果無環(huán),則fast一定比slow先到達(dá)null,不會(huì)相遇。因此,如果fast與slow相遇,則一定有環(huán)。

得證。

第一次相遇一定發(fā)生在slow第一次入環(huán)的過程中

該結(jié)論是下一步推導(dǎo)的前提

設(shè)鏈表頭節(jié)點(diǎn)為head,環(huán)入口節(jié)點(diǎn)為entrance,head到entrance共有n個(gè)節(jié)點(diǎn),環(huán)上共有m個(gè)節(jié)點(diǎn)。顯然,fast和slow相遇的節(jié)點(diǎn)一定在環(huán)中,設(shè)從entrance到這個(gè)節(jié)點(diǎn)共k個(gè)節(jié)點(diǎn)。

PS:代碼實(shí)現(xiàn)時(shí),需要關(guān)注m、n、k等計(jì)算時(shí)是否包含head、entrance等,但證明時(shí)無需關(guān)心,僅僅是加減一個(gè)常數(shù)項(xiàng)的事情。

fast的速度是slow的兩倍。則,fast第一次到entrance時(shí),slow到達(dá)鏈表中部節(jié)點(diǎn)mid,設(shè)head到mid共有s個(gè)節(jié)點(diǎn),則此時(shí),slow走了s個(gè)節(jié)點(diǎn),fast走了2s個(gè)節(jié)點(diǎn)。我們讓slow再走s個(gè)節(jié)點(diǎn),fast再走2s個(gè)節(jié)點(diǎn),分情況討論:

如果entrance等于head

此情況下環(huán)最長(zhǎng)。經(jīng)過s個(gè)節(jié)點(diǎn),slow恰好第一次到達(dá)entrance;經(jīng)過2s個(gè)節(jié)點(diǎn),fast恰好第二次到達(dá)entrance。在此過程中,slow有且僅有一次從mid走到entrance,fast有且僅有一次從head經(jīng)過mid走到entrance,從而,fast與slow必然有且僅有一次在mid于entrance之間相遇,這是二者第一次相遇。由于相遇點(diǎn)一定在環(huán)中,因此第一次相遇一定發(fā)生在slow第一次入環(huán)的過程中

如果entrance不等于head

此情況下,環(huán)均比第一種情況短。重復(fù)上述過程,slow仍然有且僅有一次從mid走到entrance,但fast卻由于環(huán)的縮短,可能不止一次與slow相遇并走到entrance。我們只關(guān)注第一次相遇,顯然,第一次相遇仍然發(fā)生在slow第一次入環(huán)的過程中

得證。

如何尋找入口節(jié)點(diǎn)

我們現(xiàn)在知道,“第一次相遇一定發(fā)生在slow第一次入環(huán)的過程中”,那么此時(shí)slow共走了n+k個(gè)節(jié)點(diǎn),fast共移動(dòng)了n+k + x*m個(gè)節(jié)點(diǎn)。fast速度是slow的2倍,則有2*(n+k) = n+k + x*m,其中x表示fast已經(jīng)在環(huán)中走的圈數(shù)。由此可得,n+k = x*m,從而n = (x-1)*m + m-k(x>=1, m>=k)。

在有環(huán)鏈表中,我們只能基于移動(dòng)和相遇進(jìn)行判斷。basic problem中,我們希望通過相遇判斷是否有環(huán),follow up中,可以試著用相遇找到entrance節(jié)點(diǎn)。

觀察式n = (x-1)*m + m-k

  • n為head到entrance的節(jié)點(diǎn)數(shù)
  • m為環(huán)的長(zhǎng)度
  • m-k為從fast、slow的相遇點(diǎn)到entrance的節(jié)點(diǎn)數(shù)

如果讓slow繼續(xù)走m-k個(gè)節(jié)點(diǎn),此時(shí)slow將恰好位于entrance;再循環(huán)y = x-1圈,仍然處于entrance。增加一個(gè)新的指針p = head,則可以這樣理解上式:使p和slow同時(shí)開始移動(dòng)(slow從剛才的相遇點(diǎn)開始),都一次移動(dòng)一個(gè)位置,則當(dāng)p第一次經(jīng)過n個(gè)節(jié)點(diǎn)走到entrance時(shí),slow恰好先經(jīng)過了m-k個(gè)節(jié)點(diǎn),再走了整y圈回到entrance

此時(shí),p與slow相遇,相遇點(diǎn)即為entrance。

代碼

/**
 * Definition for ListNode.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int val) {
 *         this.val = val;
 *         this.next = null;
 *     }
 * }
 */ 
public class Solution {
    /**
     * @param head: The first node of linked list.
     * @return: The node where the cycle begins. 
     *           if there is no cycle, return null
     */
    public ListNode detectCycle(ListNode head) {  
        // write your code here
        if (head == null) {
            return null;
        }
        
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                break;
            }
        }
        if (fast == null || fast.next == null) {
            return null;
        }
        
        ListNode p = head;
        while (p != slow) {
            p = p.next;
            slow = slow.next;
        }
        return p;
    }
}

本文鏈接:【刷題】Linked List Cycle II
作者:猴子007
出處:https://monkeysayhi.github.io
本文基于 知識(shí)共享署名-相同方式共享 4.0 國(guó)際許可協(xié)議發(fā)布,歡迎轉(zhuǎn)載,演繹或用于商業(yè)目的,但是必須保留本文的署名及鏈接。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容