题目描述
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
输入: head = [3,2,0,-4], pos = 1
输出: true
解释: 链表中有一个环,其尾部连接到第二个节点。
输入: head = [1,2], pos = 0
输出: true
解释: 链表中有一个环,其尾部连接到第一个节点。
输入: head = [1], pos = -1
输出: false
解释: 链表中没有环
思路
双指针法:快指针每次前进2步,慢指针每次前进1步,如果快慢指针相遇,则说明有环,否则,无环。
当快慢快慢指针相遇时:
-
快指针移动的距离:
x+y+k(y+z),k(y+z)表示[k圈]; -
慢指针移动的距离:
x+y
` 这里有两个问题,分别是:
- 为什么快慢指针一定会相遇?
- 为什么快慢指针一定会在慢指针进入的第一圈相遇?
解释
为什么快慢指针一定会相遇?
快指针每次移动2位,慢指针每次移动一位,在进入环之后,快指针相对于慢指针是每次移动一位,所以快慢指针一定会相遇!
为什么快慢指针一定会在慢指针进入的第一圈相遇?
首先slow进环的时候,fast一定是先进环来了。 如果slow进环入口,fast也在环入口,那么把这个环展开成直线,就是如下图的样子:
可以看出如果slow 和 fast同时在环入口开始走,一定会在环入口3相遇,slow走了一圈,fast走了两圈。
重点来了,slow进环的时候,fast一定是在环的任意一个位置,如图:
那么fast指针走到环入口3的时候,已经走了k + n 个节点,slow相应的应该走了(k + n) / 2 个节点。
因为k是小于n的(图中可以看出),所以(k + n) / 2 一定小于n。
也就是说slow一定没有走到环入口3,而fast已经到环入口3了。
说明了,在slow开始走的那一环已经和fast相遇了。
代码
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
let slow=head; //慢指针
let quick=head; //快指针
while(slow&&quick&&quick.next){
slow=slow.next;
quick=quick.next.next;
if(slow==quick){
return true;
}
}
return false;
};