题目
面试题 02.08. 环路检测
给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。若环不存在,请返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
进阶:
- 你是否可以不用额外空间解决此题?
思路1:
这一题之前在普通题库里做过,这里又遇到了,那就再做一遍。
- 用一个哈希表记录遍历过的节点如果节点在哈希表中,
- 遍历链表,如果节点在哈希表中,那么直接return该节点即可;如果不在哈希表中,则将节点放入哈希表。
- 遍历结束发现没有环,则返回null。
代码如下:
/**
* @param {ListNode} head
* @return {ListNode}
*/
var detectCycle = function(head) {
let hashMap = new Map();
let cur = head;
while(cur){
if(!hashMap.has(cur)){
hashMap.set(cur,null)
cur = cur.next;
}else{
return cur
}
}
return null;
};
思路2:
题目中写了进阶的要求,能否不使用额外的存储空间。
这里就想到了可以使用快慢指针的方法。
首先得明白这其中的数学原理。
- 假设一个fast指针每次向前走两步,一个slow指针每次向前走一步,两者同时出发,进入环后,一定会在环中某一点相遇。
- 由于fast速度是slow的两倍,当slow入环后,在一圈之内一定会被fast追上,这个仔细品一品。
- 假设两者在环中紫色的点相遇,那么slow走了a+b的距离,fast比slow多走n圈,一圈是b+c,那么fast走了a+b+n(b+c)
- 由于fast速度是slow的两倍,则可以得到等式 2(a+b)=a+b+n(b+c)
- 将等式化简一下,得到 a = (n-1)(b+c) + c
从这里我们可以得到什么启发呢?
就是假设两个节点分别从链表头和紫色点开始走,每次走一步,那么会在相交点相遇
- 首先两个节点向前走,相遇这个过程,我们用代码是可以实现的
if(fast.next == slow.next){
// 两者相遇
}else{
slow = slow.next;
fast = fast.next.next;
}
- 当两者在紫色点相遇的时候,我们新创建一个对比指针,一次走一步,那么当它两相遇的时候,返回这个相遇点即可
代码如下:
/**
* @param {ListNode} head
* @return {ListNode}
*/
var detectCycle = function(head) {
if(head === null){
return null;
}
let fast = head;
let slow = head;
while(fast !== null){
slow = slow.next;
if(fast.next){
fast = fast.next.next;
}else{
return null
};
if(fast === slow){
let test = head;
while(slow !== test){
slow = slow.next;
test = test.next;
}
return test;
}
}
return null;
};