Leetcode:142. 环形链表 II

523 阅读3分钟

力扣题目链接

题意:

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

image.png

image.png

思路(B站JS老毕):

  • 首先判断它是否有环?
    • 定义一个快指针fast和一个慢指针slow,从头节点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,若链表无环,则fast指针会先指向null。若链表有环,则fast和slow一定会在途中相遇
    • 若有环:fast指针每次走两步,slow指针每次走一步,当它们都进入环内时,假定fast指针是在slow指针的后面,那么我们的每一次操作,都会使fast指针追上slow指针一步,也就是fast更逼近slow指针一步。
    • 快慢指针会在多少步相遇?它们在进入环形开始相差几步,则快指针就需要几步能追上慢指针
  • 如果有环,如何找到这个环的入口?
    • 慢指针slow走一步,快指针fast走两步,直到慢指针入环的那一刻,看慢指针差了快指针多少步。此时快指针的路程一定是慢指针的两倍。若慢指针走了3步,则快指针一定走了6步。
    • 看它俩何时相遇?快指针每次比慢指针多走一步,即快指针每次往慢指针的方向追赶一步,则此时快指针差慢指针几步,则需要几步能追上慢指针
    • 当快慢指针相遇时,将快指针放到链表的开头,且此时快慢指针都每次走一步

image.png

  • 从链表到链表入环的节点的距离为l,当慢指针到入环节点时,快指针走过的距离为2l,设环的整个长度为d,则剩下的距离为(d-l),即快指针要追赶慢指针的步数。然后慢指针走(d-l)步,快慢指针相遇
  • 相遇后将快指针放入开头,慢指针继续在环内相遇点开始往前走,那么慢指针需要走多少步到环的起始位置呢?(d-(d-l))=l步

image.png

  • 若(l>d),即环比较短时,慢指针还没有走到入环入口,快指针已经走完环了,且在环内循环了n圈,并停在某个位置。但其实快指针在环里绕了多少圈,我们是不关心的,我们只关心它最后停在环内的哪个节点
  • 快指针和慢指针之间相隔的距离为r,l=nd+r
  • 快指针需要去追赶慢指针,它需要多少步呢?(d-r)步,此时相遇,将快指针放到头节点,慢指针继续在环里向前走。快指针从头节点走l步到环的入口。而慢指针从此地向环入口走(d-(d-r))=r步,此时并不保证快慢指针相遇,可能快指针还没走完(因为l>d,l比较长),所以慢指针可能在环里绕圈,绕完n圈,即慢指针的路程为(r+nd)=l。也就是快慢指针路程一样,此时他俩在环的入口相遇,返回这个节点

image.png

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
    if (head === null){//链表为空,则返回空
        return null
    }
    let fast = head
    let slow = head
    let isCycle = false
    
    //判断链表是否有环
    while(fast.next!=null && fast.next.next!=null){
        slow = slow.next
        fast = fast.next.next
        if(slow===fast){
            isCycle = true
            break
        }
    }
    //若链表无环,返回Null
    if(!isCycle){
        return null
    }
   //若链表有环,在快慢指针第一次相遇时,将fast指针放到链表开始
    fast = head
    while(slow!==fast){
        slow = slow.next
        fast = fast.next
    }
    return fast
};