题意:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
思路(B站JS老毕):
- 首先判断它是否有环?
- 定义一个快指针fast和一个慢指针slow,从头节点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,若链表无环,则fast指针会先指向null。若链表有环,则fast和slow一定会在途中相遇
- 若有环:fast指针每次走两步,slow指针每次走一步,当它们都进入环内时,假定fast指针是在slow指针的后面,那么我们的每一次操作,都会使fast指针追上slow指针一步,也就是fast更逼近slow指针一步。
- 快慢指针会在多少步相遇?它们在进入环形开始相差几步,则快指针就需要几步能追上慢指针
- 如果有环,如何找到这个环的入口?
- 慢指针slow走一步,快指针fast走两步,直到慢指针入环的那一刻,看慢指针差了快指针多少步。此时快指针的路程一定是慢指针的两倍。若慢指针走了3步,则快指针一定走了6步。
- 看它俩何时相遇?快指针每次比慢指针多走一步,即快指针每次往慢指针的方向追赶一步,则此时快指针差慢指针几步,则需要几步能追上慢指针
- 当快慢指针相遇时,将快指针放到链表的开头,且此时快慢指针都每次走一步
- 从链表到链表入环的节点的距离为l,当慢指针到入环节点时,快指针走过的距离为2l,设环的整个长度为d,则剩下的距离为(d-l),即快指针要追赶慢指针的步数。然后慢指针走(d-l)步,快慢指针相遇
- 相遇后将快指针放入开头,慢指针继续在环内相遇点开始往前走,那么慢指针需要走多少步到环的起始位置呢?(d-(d-l))=l步
- 若(l>d),即环比较短时,慢指针还没有走到入环入口,快指针已经走完环了,且在环内循环了n圈,并停在某个位置。但其实快指针在环里绕了多少圈,我们是不关心的,我们只关心它最后停在环内的哪个节点
- 快指针和慢指针之间相隔的距离为r,l=nd+r
- 快指针需要去追赶慢指针,它需要多少步呢?(d-r)步,此时相遇,将快指针放到头节点,慢指针继续在环里向前走。快指针从头节点走l步到环的入口。而慢指针从此地向环入口走(d-(d-r))=r步,此时并不保证快慢指针相遇,可能快指针还没走完(因为l>d,l比较长),所以慢指针可能在环里绕圈,绕完n圈,即慢指针的路程为(r+nd)=l。也就是快慢指针路程一样,此时他俩在环的入口相遇,返回这个节点
/**
* 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
};