题意
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
解题思路
① 设双指针
f
a
s
t
fast
fast 和
s
l
o
w
slow
slow ,其中
f
a
s
t
fast
fast 每次前进2步、
s
l
o
w
slow
slow每次前进1步。如果
f
a
s
t
fast
fast 最终为NULL,那么该链表无环;反之,
f
a
s
t
fast
fast 和
s
l
o
w
slow
slow必会在某一点相遇。
② 设
f
a
s
t
fast
fast 和
s
l
o
w
slow
slow相遇的点为
P
P
P,那么
s
l
o
w
slow
slow从
P
P
P点开始,
f
a
s
t
fast
fast从起始点,两个指针同时每次都前进一步,那两者最终相遇的地方即链表入环的第一个节点。
代码
/**
* 给定一个链表,如果有环路,找出环路的开始点。
*
* @author ZhangLingRan
*/
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
/**
* @author ZhangLingRan
*/
public class Solution{
public ListNode detectCycle(ListNode head) {
ListNode fast = head, slow = head;
do {
// 如果fast走到头,说明没环,那么返回NULL
if (fast == null || fast.next == null) {
return null;
}
fast = fast.next.next;
slow = slow.next;
} while (fast != slow);
//如果有环,那么找到第一次相遇的交点,slow从交点往后走、fast从head往后走,直到相遇即为交点
slow = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
方法的数学证明
假设这是一个带有环的链表;其中
O
Q
OQ
OQ的长度为
L
1
L1
L1,环的长度为
L
2
L2
L2;两个指针在
P
P
P点相遇,那么当指针
s
l
o
w
slow
slow走到了
P
P
P点,其走过的路程为
∣
O
P
∣
=
L
1
+
∣
Q
P
∣
|OP| = L1+|QP|
∣OP∣=L1+∣QP∣,那么
f
a
s
t
fast
fast指针走过的路程为
2
∣
O
P
∣
=
L
1
+
k
L
2
+
∣
Q
P
∣
2|OP| = L1+kL2+|QP|
2∣OP∣=L1+kL2+∣QP∣。
由此可知:
2
∣
O
P
∣
=
L
1
+
k
L
2
+
∣
Q
P
∣
=
2
(
L
1
+
∣
Q
P
∣
)
2|OP| = L1+kL2+|QP| = 2(L1+|QP|)
2∣OP∣=L1+kL2+∣QP∣=2(L1+∣QP∣)
化简得
k
L
2
=
∣
Q
P
∣
+
L
1
kL2 = |QP| + L1
kL2=∣QP∣+L1
即
L
1
=
∣
O
Q
∣
=
k
L
2
−
∣
Q
P
∣
=
(
k
−
1
)
L
2
+
L
2
−
∣
Q
P
∣
=
(
k
−
1
)
L
2
+
∣
P
Q
∣
L1 = |OQ| = kL2 - |QP| = (k-1)L2 + L2 - |QP| = (k-1)L2 + |PQ|
L1=∣OQ∣=kL2−∣QP∣=(k−1)L2+L2−∣QP∣=(k−1)L2+∣PQ∣
其中
∣
Q
P
∣
|QP|
∣QP∣表示从
Q
Q
Q点到
P
P
P点走过的路程,
∣
P
Q
∣
|PQ|
∣PQ∣表示从
P
P
P点到
Q
Q
Q的走过的路程。
由
L
1
=
∣
P
Q
∣
+
(
k
−
1
)
L
2
L1 = |PQ| + (k-1)L2
L1=∣PQ∣+(k−1)L2 可知,当慢指针从
O
O
O点走到了入环点
Q
Q
Q点的时候,快指针将会绕环(k-1)圈之后,与慢指针同时到达Q点。
Floyd判圈法的变形题目
题目解决方案分析:
根据题目要求,需要在包含 n + 1 个整数的数组 nums 中(其数字都在 1 到 n 之间(包括 1 和 n)),找到存在的一个重复的整数。
由题意可以知道,数组中元素的值不会超过n,那么可以根据元素来构建一个链表,即nums[0]的下一个节点为 nums[nums[0]];
由于数组存在重复元素(假设为 nums[i] 和 nums[j]),因此,在构建的这个链表中,必然有nums[i]的下一个节点为 nums[nums[i]],且nums[j]的下一个元素是nums[nums[j]]=nums[nums[i]];
因此,如果两个元素,fast和slow,使得nums[fast] = nums[slow],则fast与slow即是相同的两个元素;
如数组 [1,3,4,2,2]
那么其构成的链表结构为:
如上,最终的结果即为2;