我正在参与掘金新人创作活动,一起开启写作之路
加油 第 2 练
142. 环形链表 II
题目描述:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
此题与 环形链表I(判断是否有环相似)可以用相似的方法来解。
方法一:遍历存储
遍历‘head’,每一个遍历到的节点存储到一个容器中,如果后面遍历到已存储过的节点则说明存在环,而这第一个相同节点则为入环的第一个节点
var detectCycle = function(head) {
if (!head || !head.next) return null;
var mapper = new Map();
while(head){
if(mapper.has(head)){
return head
}
mapper.set(head, head);
head = head.next
}
return null
};
方法二:快慢指针
快慢指针可相遇则说明有环,当两指针相遇时,此时让另一指针从头节点以慢指针的速度出发,那么此指针和慢指针相遇时的节点处则为入环节点。
需要推导一下:
- 头节点到入环点距离L0;
- 入环点到首次相遇点距离L1;
- 首次相遇点到入环点距离L3;
- 相遇时慢指针走过的距离:S = L0 + L1;
- 相遇时快指针走过的距离:F = 2S = S + n(L1 + L2);
- 可以得知 L0 = (n - 1)(L1 + L2) + L2
- 头节点到入环点距离 = 从首次入环点绕环(n-1)圈,再回到入环点的距离
- 假设快慢指针相遇时slow走了k步,那么fast走了2k步;
- 由上图可看出fast比slow多走的k步,在环内转了 n 圈;
- 假设环起点到相遇点距离为m,则头节点到环起点距离为 k - m,即由头节点走 k -m 步就能到达环起点;
- 如果从相遇点继续前进 k - m 步也恰好到达环起点(从相遇点开始走k步可以转回到相遇点,那走 k - m 步肯定就走到环起点了)
- 我们把快慢指针中的任一个重新指向head,然后两个指针同速前进,k - m 步后一定会相遇,相遇之处就是环的起点了
var detectCycle = function(head) {
if (!head || !head.next) return null;
var fast = head,slow = head
while(fast){
if(!fast.next){
return null
}
slow = slow.next
fast = fast.next.next
if(fast == slow){
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
// 返回相遇时节点 -- 环起点
return fast;
}
}
return null
};