力扣142. 环形链表 II
题目来源(力扣):
https://leetcode.cn/problems/linked-list-cycle-ii/description/
题目描述:
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
基本思路:
快慢指针查找环的经典例题,
0.
为了方便起见,还是设置虚拟头结点dummyHead,它的下一个节点为真正的头结点head,即dummyHead->next=head;
这样便于处理空链表的情况
1.
设置fast、slow2个指针,fast每次走2步,slow每次走1步,如果有环,则fast一定先入环,然后以每次一步的距离与slow相遇
2.
相遇后,再次设置slow=head,快指针从刚才的相遇点继续,之后快慢指针一起移动且都只移动1步,直到它们再次相遇,此时的相遇点即为环的入口
3.
对于2的证明,在《代码随想录》中有较为完备的描述。
简单来说,
第一次相遇时,slow移动a+b,fast移动a+b+k(b+c)
其中a为不在环上的节点数量,b为slow指针在环中走过的节点数量(也是相遇点距离环入口的距离),c环中剩余节点数量,k代表fast在走过了k个环(k>=1)
由于fast移动速度为slow的2倍,所以 2(a+b) = a+b+k(b+c)
解得 a+b = k(b+c)
要求环入口,即求a
解得 a = (k-1)(b+c) + c
即,此时重新将slow设置为链表开头位置,fast从相遇点继续移动,fast和slow每次都只移动1步,
那么再次相遇的位置就是环的入口位置
4.
对3中“那么再次相遇的位置就是环的入口位置”进一步说明
首先,k一定>=1,因为fast先入环,且fast与slow相遇时,fast一定至少在环内走了一圈。
其次,为什么 将slow重新设为head并且和fast继续移动(每次一步)就必定是环入口呢?如下:
a = (k-1)(b+c) + c
k=1时,a=c,显然必定为入口
k>1时,相当于fast在环中绕k-1圈,并且走c步;
环大小为b+c,相遇点已经距离环入口处为b,再走c步就回到环人口,
因而刚好fast多走的这c步会到达环的入口处
非常巧妙~
参考《代码随想录》,细节上有细微差异,不过思路完全一致,如下:
/*
//ListNode 定义
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
*/
class Solution
{
public:
ListNode *detectCycle(ListNode *head)
{
ListNode *dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode *fas = dummyHead, *slo = dummyHead;
while (fas && fas->next)
{
fas = fas->next->next; // fast移动2步
slo = slo->next; // slow移动1步
if (fas == slo)
{ // 说明有环
// cout<<fas->val<<endl;
slo = dummyHead; // 重新设置slow
while (fas != slo)
{ // 各自移动一步,直到相遇
fas = fas->next;
slo = slo->next;
}
return fas;
}
}
return nullptr; // 无环
}
};
时间复杂度
O(n),n为链表中节点的总个数,理由如下:
当链表无环,
则fast指针以每次2步的方式走完链表,时间复杂度为O(n/2),即O(n)
当链表有环,
链表总长度为a+b+c(a为不在环上的节点数量,b为slow指针第一次移动时在环中走过的节点数量(也是相遇点距离环入口的距离),c环中剩余节点数量)
slow指针第一次走过的距离为a+b
slow指针第二次走过的距离为a = (k-1)(b+c) + c 其中k>=1
而且slow从始至终都以每次1步的方式移动,因此时间复杂度就为O(2*a+b)
而环大小b+c至少为2,即 0<=a<=链表总长度-2
因此O(n)<=O(2a+b)<O(2n)(想想为什么,可以用简单的数学知识推导),即O(n)
因此时间复杂度为O(n)
如果以上内容已经看懂了,下面的不用再看.
简单的数学知识
已知时间复杂度为O(2a+b)
a=(k-1)(b+c)+c ,k>=1
链表不在环内的部分,长度为a;环内部分长度为b+c
如果环外部分比环内小(或相同,此时b=0),则应当k=1 ,即a=c 此时时间复杂度为 O(2a+b)=O(a+b+c)=O(n) 这也是时间复杂度的下界
如果环外部分比环内大,则应当k>=2, 原链表可以看做一个长链跟着一个环形的小尾巴,
a+b <= a+b+c =n
a < a+b+c =n
因此 2a+b < 2(a+b+c) =2n ,O(2a+b)<O(2*n)这也是时间复杂度的上界
综上,O(n)<=O(2a+b)<O(2n)
从而时间复杂度为O(n)
浙公网安备 33010602011771号