例题
思路
- 如果只是判断一个链表是否成环,我们常用的是快慢指针。即让一个指针一次迈一步,另一个指针一次迈两步。如果链路承欢,则两个指针最终是会相遇的。
但是 怎么在这个基础上找到环路的开始节点呢。
我们可以再跑一次,这次同步伐(都走一步),一个从头开始,一个从快慢指针相遇的地方开始。这次的相遇即为目标节点(我们要找的环路开始节点) —这是floyd判圈算法(龟兔赛跑算法)
证明如下:
设一圈长度为l,快慢指针追上时,m+n=m+kl+n => m+n=kl(快指针追上慢指针多跑了整数倍的圈数,k为某个正整数)。
然后第二次追赶的时候,相遇即为入环的第一个节点:
因为S到A需要m B到A的话由于是圈,可以反复跑,即l-n,2l-n,…
当重复到kl-n时,才会再次相遇。
代码如下
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head, fast = head;
//while 循环跳出条件是不成环
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
// 这里是用来跳出的,即为c点
if (slow == fast) {
break;
}
}
// 这里 是接不成环的情况的
if (fast == null || fast.next == null) {
return null;
}
//这里开始第二次的相遇。
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
当然了,上面的是好方法,稍微差一点的还有一个。
- 我们可以从头开始遍历,把每次的节点都存到hashMap或者set中,一旦发现有重复,说明遇到环了,而且当前节点就是成环的开始节点。
代码如下
public class Solution {
public ListNode detectCycle(ListNode head) {
Map<ListNode,Integer> map=new HashMap<ListNode,Integer>();
while(head!=null){
if(map.getOrDefault(head,0)==0){
map.put(head,1);
}
else{
return head;
}
head=head.next;
}
return null;
}
}