Ruby's Louvre

每天学习一点点算法

导航

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]))

posted on 2020-01-07 00:38  司徒正美  阅读(995)  评论(0编辑  收藏  举报