整理部分双指针算法(重点:龟兔赛跑算法,含证明)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/L__ear/article/details/86758471

一、同速指针

问题描述: 从单链表中找到倒数第n个结点。

直觉算法

遍历一遍链表,得到链表长度 length,然后指针重新指向表头,设置从 0 开始的计数器,指针每往后移动一个,计数器加 1 ,当计数器变为 length - n 时,就找到了倒数第 n 个结点。

双指针算法

使用两个指针,假设为 p、q,p 指向表头,q 先向后移动指向第 n 个结点。然后两个指针同时向后移动,直至 q 走到链表末尾。此时 p 就指向了倒数第 n 个结点。

效率分析

两者的效率好像是类似的,只是第二种解法更新颖简洁一点(^-^)。

二、快慢指针

问题一:查找单链表中的分位点

问题描述: 查找单链表的中间结点(这是二分位点,也可以是其它分位点)

直觉算法

遍历链表获得链表长度,然后再计数获得中间结点。

快慢指针算法

使用两个指针,假设为 p、q,两者初始时都指向表头。p 一次向前移动一个结点,q 一次向前移动两个结点。当 q 移动到链表末尾,此时 p 就指向了链表的中间结点。

效率分析

效率感觉还是差不多(以取地址为基本操作进行分析),怎么搞的(0-0),第二种代码可能更简洁一点。

问题二:判圈

问题描述: 如何检测一个单链表是否有环?如果有环,如何确定环的起点以及长度?

直觉算法

用哈希表保存已经走过的结点指针,查找判断当前指针是否在之前已经出现过。

龟兔算法(Floyd cycle detection)

龟兔其实是快慢指针,假设为 p、q,初始时都指向单链表表头,p 指针每次移动一个结点,q 指针每次移动两个结点。如果 p、q 再次相遇,则说明有环。如果 q 走到链表末尾还没与 p 相遇,则无环。
floyd.png
符号约定及定义: 约定单链表表头为 A,环起点为 B,龟兔相遇结点为 C。定义 |AB| 表示沿单链表方向结点 A 到结点 B 的距离,距离值的定义为乌龟走的步数,如图 |AB| = 3。环长为 L,表示乌龟从环起点出发回到环起点所走的步数。
m = A B ( m 0 ) n = B C ( 0 n L ) m = |AB|(m\ge0),n = |BC|(0\le n\le L) 则从出发到相遇,乌龟所走的距离为: S = m + n S=m+n 后面有证明乌龟不可能绕环超过一圈。
兔子所走的距离为: 2 S = m + n + a L , a N + 2S=m+n+aL,a\in N^+ 上面两式相减,并联立乌龟所走的距离得: m + n = a L m+n=aL 由此可知,m+n是环长L的整数倍。

环起点: 当龟兔相遇后,令乌龟返回表头 A,兔子留在相遇结点 C,两者都一次向前移动一个结点(此时已是同速指针),此时乌龟往环的位置赶,兔子在环内转圈圈。当乌龟赶到环起点时,兔子也移动了 m,加上初始时距环起点的 n,兔子距环起点 m+n,为环长 L 的整数倍,此时兔子也刚好到达环起点,两者再次相遇,相遇结点就为环起点 B 。

环长度: 接着上一步,龟兔同时指向环起点,此时让两者中的一个留在 B,一个逐结点向前推进,并同步计数,当两者再次相遇就得到了环长 L。

证明快指针 q 一定会追上慢指针 p。(像是证明显然(^-^))

p 在 q 前一个结点时,两者经过一次移动两者相遇。
假设 p 在 q 前 k 个结点时,经过有限次移动两者一定会相遇。
则当 p 在 q 前 k+1 个结点时,经过一次移动,变为 p 在 q 前 k 个结点,则经过有限次移动两者一定会相遇。
得证。

证明慢指针 p 绕环不可能超过一圈。

  1. 单链表本身为环的情况
    p、q 同时从环起点(表头)出发, 当 p 绕环一圈时,由于 D q = 2 D p D_q=2D_p ,q 刚好绕环两圈,此时两者在环起点相遇。
    下面证明此前不可能相遇(出发时不算),即 p 绕环一圈时是第一次相遇:
    假设 p、q 在此之前相遇,相遇结点和环起点的距离为 n ( 0 < n < L ) n(0<n<L) ,则 D p = n D_p=n D q = n + L D_q=n+L n + L = 2 n n = L n+L=2n\Rightarrow n=L 与假设矛盾,故假设不成立。
    补充: D q = n + L D_q=n+L 需要稍稍分析一下,首先一定有 D q = n + k L , k N + D_q=n+kL,k\in N^+ 。又 D q = n + k L = 2 n < 2 L 0 < ( 2 k ) L D_q=n+kL=2n<2L\Rightarrow 0<(2-k)L ,所以 k < 2 k<2 ,即只能有 k = 1 k=1
  2. 单链表有环,但不全为环的情况
    考虑 p 追上 q 要走的最远距离的情况是,q 刚好在 p 的前一个结点。
    首先证明这种极端情况不可能发生在 p 的上一步已经在环内时(可以用于算法调优):
    假设 p 的上一步已经在环内,且在 p 的当前步出现了上述极端情况,即 q 在 p 的前一个结点。
    回退到上一步可以发现,p、q 已经相遇,算法结束。故假设不成立
    所以这种情况只能发生在 p 刚进入环起点时,q 在 p 的前一个结点。
    此时相遇之前两者之间的距离小于情况一,所以在 p 绕环一圈之前,两者就已经相遇。

效率分析

由于慢指针最多走一遍单链表,所以Floyd cycle detection的时间复杂度为O(n),空间复杂度为O(1)。与直觉算法相比少了哈希表的使用,降低了空间复杂度。

猜你喜欢

转载自blog.csdn.net/L__ear/article/details/86758471