链表学习二
链表中环的问题
判断是否有环
- 遍历链表加入hash,发生碰撞则说明有环;
- 使用快慢指针,如果相遇则说明有环;(有环一定会相遇,无环快的指针先到末尾)
确定环的入口
- 同上述方法一,第一次发生碰撞的节点即为环的入口;
public static ListNode detectCycleByMap(ListNode head) {
ListNode pos = head;
Set<ListNode> visited = new HashSet<ListNode>();
while (pos != null) {
if (visited.contains(pos)) {
return pos;
} else {
visited.add(pos);
}
pos = pos.next;
}
return null;
}
- 快慢指针法。
结论:找到第一次相遇的位置,之后一个指针在入口出发,另一个在相遇点以同速率出发,再次相遇时为环的入口。
由题意可知慢指针在环里移动不会超过一圈,假设慢指针走了a+b
距离,则快指针走了a+n(b+c)+b
的距离(n为快指针走的圈数),有a+n(b+c)+b=2(a+b)
,得出a=c+(n-1)(b+c)
,n为1时,说明a=c
,则第一次相遇的位置,之后一个指针在入口出发,另一个在相遇点以同速率出发,再次相遇时为环的入口
是正确的;n为2,3,4…时,说明一个指针在a上移动时,另一个指针转了(n-1)圈后又走了c的距离,恰好等于a,即在环的入口相遇。
public static ListNode detectCycleByTwoPoint(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head;
while (fast != null) {
slow = slow.next;
if (fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if (fast == slow) {
ListNode ptr = head;
while (ptr != slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
return null;
}
- 三次双指针方法
在知道环的长度和环的尾结点时,就能够通过倒数第K个元素的方法找到环的入口了。
第一次:通过快慢指针是否相遇来判断是否存在环;
第二次:在两个指针相遇点,一个指针不动,另一个指针继续走直到再次相遇能够得到环的长度L;
第三次:从起点开始,fast指针先走L,之后两指针以共同速率走,直到两指针再次相遇即为入口。