双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。1
相同方向(快慢指针)
在求解相关的问题的过程中,需要两个指针:快指针和慢指针。两个指针开始都指向开头,根据条件不同,快指针走得快,慢指针走的慢,直到满足条件或者快指针走到结尾。2
问题:找出成环的链表中入环的结点
要求 : 空间复杂度为O(1)
参考题目:LeetCode 142. 环形链表 II
显然这个题目可以采用如下方法:
用一个指针从头开始遍历,并将遍历的值存放在一个集合中,直到找到第一个已经存在的元素为止。此时算法空间复杂度为O(n)。
当应用场景需要空间复杂度为O(1)时,单指针显然无法满足需求。此时可以借助双指针进行求解。
画出双指针跑动时的示意图如下。为了便于理解,此处用“距离”代指两个结点之间需要经过多少个结点,即需要next几次才能到达。
让两个指针同时从起点(头结点)出发,快指针的速度是慢指针的2倍。两个指针首次相遇时,两个指针与起点的距离为 ( a + b ) (a+b) (a+b).
此时,
慢指针尚未走完一圈,走过的距离为
(
a
+
b
)
(a+b)
(a+b);
快指针已经走过了
n
n
n圈,走过的距离为
a
+
b
+
n
(
b
+
c
)
=
a
+
(
n
+
1
)
b
+
n
c
a + b + n(b+c) = a + (n+1)b + nc
a+b+n(b+c)=a+(n+1)b+nc
同时由于快指针任何时刻走过的距离均为慢指针的2倍,因此快指针的距离也可以写成
2
(
a
+
b
)
2(a+b)
2(a+b)
因此,
2
(
a
+
b
)
=
a
+
(
n
+
1
)
b
+
n
c
⇒
a
=
(
n
−
1
)
b
+
n
c
⇒
a
=
c
+
(
n
−
1
)
(
b
+
c
)
\begin{array}{crcl} &2(a+b) &= &a + (n+1)b + nc \\ \Rightarrow & a &=& (n-1)b + nc \\ \Rightarrow & a & = & c + (n-1)(b+c) \\ \end{array}
⇒⇒2(a+b)aa===a+(n+1)b+nc(n−1)b+ncc+(n−1)(b+c)
可以发现,从相遇的位置到入环位置的距离
c
c
c加上
(
n
−
1
)
(n-1)
(n−1)圈的环长
(
b
+
c
)
(b+c)
(b+c),恰好等于起点到入环位置的距离。因此,在相遇点和起点各放置一个每次只走一步的指针,下一次的相遇位置就是入环的位置。
综上可知,求解题目的思路如下:
- 定义 f a s t fast fast、 s l o w slow slow指向头结点,让它们一直往下遍历。其中 f a s t fast fast的遍历一次为2个结点,直到两个结点的值相等;
- 定义 i n L i s t inList inList指针指向头结点, s l o w slow slow和 i n L i s t inList inList同时向下开始遍历,直到两个结点的值相等;
- i n L i s t inList inList (或 s l o w slow slow) 所指的结点即为入环的位置。