搞懂链表环路-快慢指针:原理和代码
快慢指针经常被用在链表相关的一些算法中,具有很高的时间和空间效率。最近在LeetCode上刷题看到了链表环路检测和环路起始节点的访问,看了一些代码和一些大佬的解释,但是感觉说得不是很清楚。所以自己动手推导了一下。
1问题抽象
带有环的链表一定可以表示成下面这种模型。这张图是我从LeetCode一个作者那里抄过来的。
图中a是链表的起点,b是环的起点,c是快慢指针相遇的点,x是a到b经历的节点数,y是b到c经历的节点数,z是c到b的节点数。那么整个链表的长度为
L
=
x
+
y
+
z
L=x+y+z
L=x+y+z.
2快指针是慢指针的2倍速度
如果将快指针设定为每次前进两步,慢指针每次前进一步,那么考虑三个问题:
1.慢指针到达c时,经历的步数为:
S
(
慢
)
=
x
+
y
S(慢)=x+y
S(慢)=x+y
2.快指针到达c时,经历的步数为:
S
(
快
)
=
(
L
+
y
)
/
2
=
(
x
+
z
y
+
z
)
/
2
S(快)=(L+y)/2=(x+zy+z)/2
S(快)=(L+y)/2=(x+zy+z)/2
3.快慢指针是同步控制的,显然经历的步数应该相等。也就是
S
(
快
)
=
S
(
慢
)
S(快)=S(慢)
S(快)=S(慢),将相关数值代入有:
x
+
y
=
(
x
+
z
y
+
z
)
/
2
x+y=(x+zy+z)/2
x+y=(x+zy+z)/2化简居然得到
x
=
z
x=z
x=z
也就是说,按照这种方式遍历,当快慢指针相遇时,从链表头到环起点的节点数与第一次相遇点到环起点的节点数相同!
这也就意味着,要找出b,只需要用两个指针,分别从a和c开始每次走一步,两个指针相遇时就找到了b.
可以总结,这个找到链表环起点的方法干了两件事:
1.找c,即找等距点;
2.找b,即找环起点。
3 C++代码描述
ListNode *detectCycle(ListNode *head) {
if(!head)return NULL;//防止空链表
ListNode* fast=head;
ListNode* slow=head;
do
{
fast=fast->next;
if(!fast)return NULL;//防止无环链表,fast前进第一步
fast=fast->next;
if(!fast)return NULL;//防止无环链表,fast前进第二步
slow=slow->next;//slow前进一步
}while(fast!=slow);
//这里已经找到了c,fast和slow都指向了c
//接下来开始找出b
fast=head;
while(fast!=slow)
{
fast=fast->next;
slow=slow->next;
}
return fast;
}
4 快指针是慢指针的三倍速
不妨做一个探讨,让快指针一次前进三步,但慢指针仍然只前进一步。仍然考虑原来的三个问题:
1.慢指针到达c时,经历的步数为:
S
(
慢
)
=
x
+
y
S(慢)=x+y
S(慢)=x+y
2.快指针到达c时,经历的步数为:
S
(
快
)
=
(
L
+
y
)
/
2
=
(
x
+
z
y
+
z
)
/
3
S(快)=(L+y)/2=(x+zy+z)/3
S(快)=(L+y)/2=(x+zy+z)/3
3.快慢指针是同步控制的,显然经历的步数应该相等。也就是
S
(
快
)
=
S
(
慢
)
S(快)=S(慢)
S(快)=S(慢),将相关数值代入有:
x
+
y
=
(
x
+
z
y
+
z
)
/
2
x+y=(x+zy+z)/2
x+y=(x+zy+z)/2化简居然得到
2
x
+
y
=
z
2x+y=z
2x+y=z
这个时候,问题就复杂了因为不能在顺序访问就找出环的起点了。换一个思路,2倍速之所以成功,是因为消去了方程中的y.
5二倍速算法中c点的存在性
我们上面考虑的三个问题其实有一个暗含的假设,就是一定存在c点,也就是说当快指针一次前进两步,会在一定会在某个时候追上一次只前进一步的慢指针。
然而,这件事情真的一定会发生吗?
至少有一件事情我们可以确信,如果不加控制,链表中存在环,快指针一定会反复地超过慢指针。这个很显而易见。
那么,这个存在性问题可以简单地等同于一个对齐问题,由于快指针只比慢指针多一步,那么当快指针即将接近慢指针时,只可能有下面两种情况:
… | fast | ||
---|---|---|---|
… | … | slow | … |
… | fast | ||
---|---|---|---|
… | slow |
(表格每一个代表一个节点,不太好画表格,狗头)
(1)如果是第一种情况,那么在经过一次追赶,fast会前进两步,slow只会前进一步,最终相遇;
(2)如果是第二种情况,已经相遇;
其余的情况最终都会到这两步,很明显,如果不清楚试着写一写?
因此,一次前进两步的快指针一定会和一次前进一步的慢指针相遇。当然在我们的大前提下呀。
6重要的事
觉得不错请点赞,收藏或者评论,当然打赏也是可以的。
转载请注明出处。
打击盗版,鼓励创作,靠我们