这篇文章主要讨论
首先,问题141和142都是不保证链表一定有环路,因此我们首先需要判断链表是否有环路。其次对于142,还要判断链表环路开始。
双指针判断是否有环路
设置一个快指fast
针和一个慢指针slow
,初始都指向head
。后面fast
每次移动两步,slow
每次移动一步。加如链表有环,在某一个时间,fast
一定会追上slow
,和slow
重合,且绝对不会跳过slow
。因为fast
的速度为2
,slow
速度为1
,以slow
为参考系,因此fast
相对于slow
的速度为fast-slow=1
。fast
相对slow
每次只向前一步,因此不会跳过slow
。
因此141的答案可以写成这样,要注意fast
前进时,需要分别判断fast.Next
,fast.Next.Next
是否为空。然后因为fast
快于slow
,因此fast
探路完了不需要再判断slow
。
func hasCycle(head *ListNode) bool {
if head==nil{
return false
}
started:=false
fast,slow:=head,head
for !started || fast!=slow {
started=true
if fast.Next==nil || fast.Next.Next==nil{
return false
}else{
fast=fast.Next.Next
}
slow=slow.Next
}
return true
}
判断环路起始点
这里借用官方图,链表头head
到入环点距离为a
。b+c
为一个完整的环长,fast
将在入环后转了n
圈后与slow
相撞。
假设fast
在环里转了n
圈后和slow
相撞,fast
走过的路程可以表示为f=a+n(b+c)+b
。此外,这里先说明一个事实,slow
一定会在入环后第一圈内和fast
相撞,这里我们先不证明,后面再解释。基于这个事实,我们可以得出slow
走过的路程为s=a+b
。此外,我们知道fast
和slow
从一个起点出发,fast
速度是slow
的两倍,因此,fast
和slow
相撞时,fast
的路程一定是slow
的两倍,即f=2s
。
基于上述公司,开始推理。因为我想知道链表头起始点,我们要关注a
。
2s=f
2(a+b)=a+n(b+c)+b
a=n(b+c)-b
a=(n-1)(b+c)+c
因此可以得出,链表头移动到入环点的距离a
等于在环内转了n-1
圈后再走了距离c
。这个可能难以理解,但是我们知道走完一次b+c
相当于转了一圈,也就是停留在原地,因此我们可以把(n-1)(b+c)
约掉。即变成a=c
。或者可以这样想,fast
如果只转了一圈后就撞上slow
,即n=1
,那可得a=c
。
好,现在我们只要求出c
就行了。因为相撞点到入环点的距离等于链表头到入环点的距离。所以我设置一个指针p
,从链表头开始,和slow
每次移动一格,最后一定会在入环点相撞。
因此142的代码
func detectCycle(head *ListNode) *ListNode {
if head==nil{
return nil
}
started:=false
fast,slow:=head,head
for !started || fast!=slow {
started=true
if fast.Next==nil || fast.Next.Next==nil{
return nil
}else{
fast=fast.Next.Next
}
slow=slow.Next
}
p:=head
for p!=slow{
slow=slow.Next
p=p.Next
}
return p
}
为什么slow一定会在第一圈内相撞
其实这个想法是官解评论区看到的,我们可以通过转换参考系的办法来思考。已知fast
速度为2
,slow
速度为1
,因此fast
相对于slow
的速度为fast-slow=1
。上文也说过,这也是为什么fast
一定会撞上slow
而不会跳过。
假设slow
没有在进入环的第一时间和fast
相撞,那此时在环内,fast
和slow
一定相隔一段距离。这是一段废话,因为不相撞肯定就相隔距离。假设环周长为d
,那么,fast
和slow
的最大距离只能为d-1
。
这个时候,我们转换参考系,以slow
为参考系。根据上文的结论,slow
和fast
最多相隔d-1
距离,而且fast
和slow
相对速度为1
。将根据匀速运动公式时间=路程/速度
,得出slow
最多可以移动的时间为(d-1)/1
,即d-1
。这时把参考系转换为整个链表,已知slow
速度为1
,可以走的时间为d-1
,即可以走d-1
次。还是根据匀速运动公式路程=时间*速度
。所以能走的路程为(d-1)*1
即d-1
,也就是走不完一个完整环长就会和fast
相撞。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。