leetcode 457. Circular Array Loop


先回顧一下鏈表的類似問題

leetcode 141 判定鏈表是否有環

慢指針slowPtr每次后移1個結點。快指針fastPtr每次后移2個結點

   function isLinkedListContainsLoop( head){
        if(head==null){
            return false;
        }
        let slowPtr=head;
        let fastPtr=head;
        while(slowPtr.next!=null && fastPtr.next.next!=null){
            slowPtr=slowPtr.next;
            fastPtr=fastPtr.next.next;
            if(slowPtr==fastPtr){
                return true;
            }
        }
        return false;
    }

LeetCode 142 找出環的入口點(起點)

當fast按照每次2步,slow每次一步的方式走,發現fastPtr和slowPtr重合,確定了單向鏈表有環路。接下來,讓slowPrt回到鏈表的頭部,然后slowPtr和fastPtr各自從自己的位置(fastPtr從兩個指針相遇的位置position出發)沿着鏈表出發,每次步長1,那么當fastPtr和slowPtr再次相遇的時候,就是環路的入口了。

function findLinkedListLoopBegin(head) {
            if (head == null) {
                return null;
            }
            let slowPtr = head;
            let fastPtr = head;
            let isLinkedListContainsLoop = false;
            while (slowPtr.next != null && fastPtr.next.next != null) {
                slowPtr = slowPtr.next;
                fastPtr = fastPtr.next.next;
                if (slowPtr == fastPtr) {
                    isLinkedListContainsLoop = true;
                    break;
                }
            }
            if (isLinkedListContainsLoop) {
                slowPtr = head;
                let count = 1
                while (slowPtr == fastPtr) {
                    slowPtr = slowPtr.next;
                    fastPtr = fastPtr.next;
                    count++
                }
                return slowPtr;
            }
            return null;
        }

設環長為n,非環形部分長度為m,當第一次相遇時顯然slow指針行走了 m+An+k(A表示slow行走了A圈。附:An 是因為如果環夠大,則他們的相遇需要經過好幾環才相遇)。fast行走了 m+B*n+k。

上面我們說了slow每次行走一步,fast每次行走兩步,則在同一時間,fast行走的路程是slow的兩倍。假設slow行走的路程為S,則fast行走的路程為2S。

用fast減去slow可得:

S=(B-A)*n

很顯然這意味着當slow和fast相遇時他們走過的路程都為圈長的倍數。

接下來,將slow移動到起點位置,如下圖:

然后每次兩個指針都只移動一步,當slow移動了m,即到達了環的起點位置,此時fast總共移動了 2S+m。 考慮到S為環長的倍數,可以理解為:fast先從鏈表起點出發,經過了m到達環的起點,然后繞着環移動了幾圈,最終又到達環的起點,值為2S+m。所以fast最終必定處在環的起點位置。即兩者相遇點即為環的起點位置。

衍生問題2,求環的大小(長度)

當fast按照每次2步,slow每次一步的方式走,發現fastPtr和slowPtr重合,確定了單向鏈表有環路。接下來,讓slowPrt不動,fast 繞着環移動,每次移動一步,計數count加1,當兩指針再次相遇時,count即是環的大小

回歸原題,也是用快慢節點

function circularArrayLoop(nums) {
            let n = nums.length;
            if (n <= 1) {
                return false
            }
            function getNext(i) {
                return (i + nums[i] + n) % n
            }

            for (let i = 0; i < n; i++) {
                let slow = i, fast = getNext(i);
                //確保總是朝着一個方向前進
                while (nums[slow] * nums[i] > 0 && nums[fast] * nums[i] > 0) {
                    if (slow == fast) {
                        //判斷是否只有一個元素
                        if (slow == getNext(slow)) {
                            break;
                        }
                        return true;
                    }
                    slow = getNext(slow);
                    fast = getNext(fast);//快指針每次走兩步
                    if (nums[fast] * nums[i] < 0) {//如果方向反了
                        break;
                    }
                    fast = getNext(fast);
                }
            }
            return false;
        }

        console.log(circularArrayLoop([2, -1, 1, 2, 2]))
        console.log(circularArrayLoop([-1, 2]))
        console.log(circularArrayLoop([-2, 1, -1, -2, -2]))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM