【刷算法】判断链表是否有环以及返回入环节点

题目描述

判断一个单链表是否有环,有环则返回入环节点,不然返回nulloop

1->2->3->4->5->6
            ↑  ↓
            8<-7

例如上面这个链表就有环,入环节点是5ui

判断链表有环

一般判断链表是否有环,会采用快慢指针的方法,其实道理很简单,就像两我的赛跑且一我的跑得快一我的跑得慢。若是赛道是直的,那么快人跑到终点时慢人还未到;若是赛道是环形,则快人和慢人总会相遇。
代码实现this

function ListNode(x){
    this.val = x;
    this.next = null;
}
function EntryNodeOfLoop(pHead){
if(pHead === null)
    return null;
// 快慢指针从链表的头部开始
var fast = pHead;
var slow = pHead;

while(fast.next !==null && fast.next.next !== null) {
// 快指针每次走两步;慢指针每次走一步
    slow = slow.next;
    fast = fast.next.next;
    // 快慢指针相遇时,跳出while循环
    if(slow === fast)
        break;
}
// 快指针已经到了链表尾部了还没和慢指针相遇,说明没有环
if(fast === null || fast.next === null)
    return null;
    
// 后续会处理有环的状况...
}

找到入环节点

常见的方法是:在肯定链表有环以后,慢指针从新指向链表头,快指针留在相遇处;而后快慢指针再以每次移动1个节点的速度前进,最终他们在入环节点相遇。
为何这么作就能够保证在入环节点相遇?证实一下:
图片描述
如图,设整个链表长度为L,环长度为R,且距离具备方向性,例如CB是C点到B点的距离,BC是B点到C点的距离,CB!=BC。当证实有环时,fast和slow都顺时针到了B点,则此时:
slow走的距离:AC+CB
fast走的距离:AC+k*R+CB(k=0,1,2...)
因为fast每次走2个节点,slow每次走1个节点,因此:
2(AC+CB) = AC+k*R+CB
AC+CB = k*R
AC+CB = (k-1)*R+R
AC = (k-1)*R+R-CB
AC = (k-1)*R+BC
从最终的表达式能够看出来,AC的距离等于绕环若干圈后再加上BC的距离,也就是说慢指针从A点出发以速度1前进、快指针从B点出发以速度1前进,则慢指针到C点时,快指针也必然到了。
代码实现:spa

function ListNode(x){
    this.val = x;
    this.next = null;
}
function EntryNodeOfLoop(pHead){
    if(pHead === null)
        return null;
    var fast = pHead;
    var slow = pHead;

    while(fast.next !==null && fast.next.next !== null) {
        slow = slow.next;
        fast = fast.next.next;
        if(slow === fast)
            break;
    }

    if(fast === null || fast.next === null)
        return null;
    // 有环,slow从新回到链表头
    slow = pHead;
    
    // slow和fast从新相遇时,相遇节点就是入环节点
    while(slow !== fast) {
        slow = slow.next;
        fast = fast.next;
    }

    return slow;
}
相关文章
相关标签/搜索