题目链接
Leetcode.剑指 Offer II 022 链表中环的入口节点 mid
题目描述
给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next
指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null
。
为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0
开始)。 如果 pos
是 -1
,则在该链表中没有环。注意,pos
仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
提示:
- 链表中节点的数目范围在范围 [ 0 , 1 0 4 0, 10^4 0,104] 内
- − 1 0 5 < = N o d e . v a l < = 1 0 5 -10^5 <= Node.val <= 10^5 −105<=Node.val<=105
pos
的值为-1
或者链表中的一个有效索引
分析:快慢指针
我们用两个指针 fast
和slow
,初始都指向 head
,fast
每次走两步,slow
每次走一步。
如果链表存在环,那么 fast
和 slow
一定会在环中相遇。
因为fast
比slow
要快1步,所以当 slow
走过的距离为 x + y
到达相遇点时,fast
其实已经在环里转了若干圈了(这里假设是 n
圈)。
所以 fast
走过的路程为 ,
x
+
n
∗
(
y
+
z
)
+
y
x + n * (y + z) + y
x+n∗(y+z)+y
又因为 fast
走过的路程 应该是 两倍slow
走过的路程,即
x
+
n
∗
(
y
+
z
)
+
y
=
2
∗
(
x
+
y
)
x + n * (y + z) + y = 2 * (x + y)
x+n∗(y+z)+y=2∗(x+y)
化简得 :
x
=
(
n
−
1
)
∗
(
y
+
z
)
+
z
x = (n - 1) * (y + z) + z
x=(n−1)∗(y+z)+z,即从相遇点走 z
的路程,再走若干圈,就是 x
的路程。(我们只需要走 0 圈即可),即
x
=
z
x = z
x=z。
当 fast
和 slow
相遇时,让 fast
重新指向头节点 head
,fast
和slow
同时移动,当他们再次相遇时的点,就是环的起点。
时间复杂度: O ( n ) O(n) O(n)
C++代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head == nullptr) return nullptr;
ListNode *fast = head , *slow = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
//两者相遇
if(slow == fast){
fast = head;
while(slow != fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return nullptr;
}
};
Java代码:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null) return null;
ListNode fast = head;
ListNode slow = head;
//fast 或 fast.next 为 null , 说明链表没有环
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
//快慢指针相遇了,fast 重新回到头节点 head,快慢指针再同时移动
if(slow == fast){
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
}