1.链表中环的问题
1.1.判断是否有环
可以用hash或者set来做,思路比较简单。
空间复杂度为O(1):使用快慢双指针,若有环,则两指针必定会相遇。
public boolean hasCycle(ListNode head) {
//利用set的不可重复解决,将遍历过的节点存入set,若不能存入,则说回到了已经遍历过的节点上,有环
//Set<ListNode> set=new HashSet<ListNode>();
// while(head!=null){
// if(!set.add(head)){
// return true;
// }
// head=head.next;
// }
// return false;
if(head == null || head.next == null) return false;
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(slow == fast) return true;
}
return false;
}
1.2.判断是否有环,并返回环的入口
结论:还是快慢双指针,我们需要制造两次相遇,在第一次相遇后,fast指向头节点,slow指向第一次相遇出,两者以相同速度前进,相遇时,此处就是环的入口。
为什么?
在Z处相遇,
步数:fast = a+b+c+b; slow = a+b;
fast步数是slow的两倍,所以2*(a+b) = a+b+c+b。可以得到a=c。
也就是说,在第一次相遇后,fast指向头节点,slow指向第一次相遇出,两者以相同速度前进,相遇时,此处就是环的入口。
多圈后相遇也是一样的,区别是a对于c多加了n圈的长度(a=c+(n-1)len)
public ListNode detectCycle(ListNode head) {
ListNode fast = head,slow = head;
// 构建第一次相遇(fast 2步,slow 1步)
while(true){
// 没有环
if(fast == null || fast.next == null) return null;
fast = fast.next.next;
slow = slow.next;
if(slow == fast) break;
}
// 构建第二次相遇,相遇处就是环的入口
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
slow = slow.next;
}
return fast;
}