题目地址:
https://leetcode.com/problems/linked-list-cycle/
链表求环,返回环是否存在。经典的快慢指针算法。如果链表没有node,或者链表只有一个node且其未指向自己,则返回false。否则设快慢指针slow和fast初始化为head,slow每次走一步,fast每次走两步,如果某一时刻fast = null
或者fast.next = null
,说明没有环,如果slow = fast
说明有环。两种情况下都退出,然后判断一下是哪种情况即可。代码如下:
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head, fast = head;
do {
slow = slow.next;
// head.next != null,所以这一句第一次执行时不会NPE
fast = fast.next.next;
} while (fast != null && fast.next != null && slow != fast);
if (fast == null || fast.next == null) {
return false;
}
return true;
}
}
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
时间复杂度 O ( n ) O(n) O(n)。
算法正确性证明:
如果链表为null或者链表只有一个node,即head,且head.next不为自己,则没有环,算法正确。对于链表长度大于等于
2
2
2的情形:
由于fast每次走两步,如果链表无环,那么fast会先于slow到达表尾,在某一时刻有fast == null
或者fast.next == null
,取决于链表长度的奇偶性。所以此程序对链表无环的情况是正确的。
如果链表有环,设链表的nodes是这样排列:
v
1
→
v
2
→
.
.
.
→
v
k
−
1
→
v
k
→
v
k
+
1
→
.
.
.
→
v
k
+
s
→
v
k
v_1\rightarrow v_2 \rightarrow ...\rightarrow v_{k-1} \rightarrow v_k\rightarrow v_{k+1}\rightarrow ...\rightarrow v_{k+s}\rightarrow v_k
v1→v2→...→vk−1→vk→vk+1→...→vk+s→vk也就是说从
v
k
v_k
vk到
v
k
+
s
v_{k+s}
vk+s形成一个环。由于slow迟早会等于
v
k
v_k
vk,当slow等于
v
k
v_k
vk时,设fast等于
v
k
+
r
v_{k+r}
vk+r,此时想象一下,如果slow不动,fast每次走一步,那么需要
s
−
r
+
1
s-r+1
s−r+1步fast就能追上slow,也就是fast == slow
;现在fast每次走两步,slow每次走一步,每次循环,它们的差距就减少
1
1
1,所以也是
s
−
r
+
1
s-r+1
s−r+1步就会有fast == slow
。所以如果链表有环,程序也是正确的。
因为链表要么无环,要么有环,所以while循环一定会退出,所以程序正确。