141.环形链表
题目
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
思路(快慢指针法)
- 假设链表长度为 m m m,其中环的长度为 n n n.其中 0 ≤ n ≤ m {0}{\le}{n}{\le}{m} 0≤n≤m,当 n = 0 n=0 n=0时,此时链表中没有环.
- 快慢指针都从头节点
head
出发,其中快指针fast
每次向前移动2个结点,慢指针slow
每次向前运动1个结点. - 当快慢指针在第 x x x步相遇时,说明链表中存在环.
原理:快慢指针在离环的第一个结点共有
c
c
c结点时相遇.慢指针已经走了
x
x
x步,快指针已经走了
2
x
2x
2x步,此时应当满足方程:
{
x
=
c
+
(
m
−
n
)
+
k
1
n
k
1
∈
N
2
x
+
1
=
c
+
(
m
−
n
)
+
k
2
n
k
2
∈
N
\begin{cases} x=c+(m-n)+k_1n &k_1\in \mathbb{N}\\ 2x+1=c+(m-n)+k_2n &k_2\in \mathbb{N} \end{cases}
{x=c+(m−n)+k1n2x+1=c+(m−n)+k2nk1∈Nk2∈N
其中
0
≤
c
≤
n
≤
m
0\le{c}{\le}{n}{\le}{m}
0≤c≤n≤m.
由上述式子可以得到,只需找到关于未知数
x
,
c
,
k
1
,
k
2
x,c,k_1,k_2
x,c,k1,k2满足方程
{
x
=
(
k
2
−
k
1
)
n
−
1
(
k
2
−
2
k
1
+
1
)
n
=
m
+
c
+
1
\begin{cases} x=(k_2-k_1)n-1\\ (k_2-2k_1+1)n=m+c+1 \end{cases}
{x=(k2−k1)n−1(k2−2k1+1)n=m+c+1
的解,就可以说明快慢指针一定能够相遇.特别地,当
n
=
0
n=0
n=0时,此时第二个方程不成立,链表没有环.
若
n
≠
0
n\ne0
n=0,方程
x
=
(
k
2
−
k
1
)
n
x=(k_2-k_1)n
x=(k2−k1)n显然能够成立,此时只需
(
k
2
−
2
k
1
+
1
)
n
=
m
+
c
+
1
(k_2-2k_1+1)n=m+c+1
(k2−2k1+1)n=m+c+1也成立即可.又因为
m
≤
c
+
m
≤
n
+
m
{m}{\le}{c+m}{\le}{n+m}
m≤c+m≤n+m,此时区间
[
m
+
1
,
n
+
m
+
1
]
[m+1,n+m+1]
[m+1,n+m+1]的长度为
n
n
n,说明一定有一个整数满足是
n
n
n的倍数,即上述方程
(
k
2
−
2
k
1
+
1
)
n
=
m
+
c
+
1
(k_2-2k_1+1)n=m+c+1
(k2−2k1+1)n=m+c+1也成立.说明链表中存在环.
代码
public class Solution {
public boolean hasCycle(ListNode head) {
// 若链表为空或只有一个结点,则不存在环,返回false
if (head == null || head.next == null){
return false;
}
ListNode slow = head, fast = head.next;
// 快慢指针相遇时,说明链表中存在环
while (slow != fast){
if (fast == null || fast.next == null){
return false;
}
slow = slow.next;//慢指针每次向前移动1个结点
fast = fast.next.next;//快指针每次向前移动2个结点
}
return true;
}
}
142.环形链表II
题目
给定一个链表,返回链表开始入环的第一个节点。如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
示例1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
快慢指针法
- 仿照上题,当快慢指针进行相遇时。此时慢指针已经走了 x = ( k 2 − k 1 ) n − 1 x=(k_2-k_1)n-1 x=(k2−k1)n−1,如果让慢指针再走 m − n + 1 m-n+1 m−n+1步时,此时慢指针走了 x = m + ( k 2 − k 1 ) n x=m+(k_2-k_1)n x=m+(k2−k1)n。长度刚好为整个队长加上环长度的整数倍,此时指针指向指向入环的第一个结点。
- 当快慢指针进行相遇时,慢指针移动一个结点后。此时让一个新指针从头节点出发,经过 m − n m-n m−n步时,此时新指针刚好指向入环的第一个结点。说明此时新指针和慢指针在入环的第一个结点相遇。
代码
public class Solution {
public ListNode detectCycle(ListNode head) {
// 若链表为空或只有一个结点,则不存在环,返回false
if (head == null || head.next == null){
return null;
}
ListNode slow = head, fast = head.next;
// 快慢指针相遇时,说明链表中存在环
while (slow != fast){
if (fast == null || fast.next == null){
return null;
}
slow = slow.next;//慢指针每次向前移动1个结点
fast = fast.next.next;//快指针每次向前移动2个结点
}
//快慢结点相遇后,令一个指针指向头节点,另一个指向慢指针下一个结点
ListNode a = head;
ListNode b = slow.next;
while (a != b){
a=a.next;
b=b.next;
}
return a;
}
}