为什么在链表中找到循环时为什么要将指针增加2,为什么不3,4,5呢?

GG.*_*GG. 51 algorithm linked-list cycle data-structures floyd-cycle-finding

我已经看过一个问题,讨论在链表中查找循环的算法.我已经阅读了Floyd的循环寻找算法解决方案,在许多地方提到我们必须采取两个指针.一个指针(慢/龟)增加一个,其他指针(更快/野兔)增加2.当它们相等时我们找到循环,如果更快的指针到达null,则链表中没有循环.

现在我的问题是为什么我们将指针增加更快2.为什么不是别的呢?增加2是必要的,或者我们可以将它增加X来得到结果.如果我们将指针增加2,或者可能存在需要增加3或5或x的情况,我们是否有必要找到一个循环.

tem*_*def 52

你没有理由需要使用二号.任何步长选择都可行(当然除了一个).

为了了解其工作原理,让我们先来看看为什么Floyd的算法起作用.想法是考虑列表的开头,然后从列表的开头然后访问的链接列表的元素的序列x 0,x 1,x 2,...,x n,...继续往下走,直到你到达终点.如果列表不包含循环,则所有这些值都是不同的.但是,如果它确实包含一个循环,那么这个序列将无休止地重复.

这是使Floyd算法工作的定理:

链接列表包含一个周期当且仅当有一个正整数j,使得对于任何正整数k,X Ĵ = X JK.

我们来证明这一点; 这并不难.对于"if"情况,如果存在这样的aj,则选择k = 2.然后我们得到一些正j,x j = x 2j和j≠2j,因此列表包含一个循环.

对于另一个方向,假设列表包含从位置s开始的长度为l的循环.设j是l大于s的最小倍数.然后对于任何k,如果我们考虑x j和x jk,因为j是循环长度的倍数,我们可以将x jk视为通过从列表中的位置j开始形成的元素,然后采用j步k-1倍.但是每次你采取j步,你最后都会回到你在列表中开始的位置,因为j是循环长度的倍数.因此,x j = x jk.

这个证明可以保证,如果你在每次迭代中采用任意数量的步骤,你确实会遇到慢速指针.更确切地说,如果你在每次迭代中采取k步,那么你最终将找到点x j和x kj并将检测周期.直观地,人们倾向于选择k = 2来最小化运行时间,因为在每次迭代时采用最少的步数.

我们可以更正式地分析运行时,如下所示.如果列表不包含循环,则快速指针将在n(n)次的n步之后到达列表的末尾,其中n是列表中的元素数.否则,在慢速指针采取j步之后,两个指针将会相遇.请记住,j是l大于s的最小倍数.如果s≤l,则j = 1; 否则如果s> l,则j最多为2s,因此j的值为O(s + 1).由于l和s可以不大于列表中元素的数量,这意味着比j = O(n).但是,在慢速指针经过j步之后,快速指针将对较慢指针所采用的每个j步进行k步,因此它将采取O(kj)步.由于j = O(n),净运行时间最多为O(nk).请注意,这表示我们使用快速指针执行的步骤越多,算法完成所需的时间越长(尽管只是按比例).挑选k = 2因此最小化算法的整体运行时间.

希望这可以帮助!

  • 对于那些贬低的人 - 你能解释这个答案有什么问题吗? (3认同)
  • 美丽的解释.它盯着"让j是l大于s的最小倍数"一分钟后,它点击了:这意味着如果从开始采取j步,你就在循环内(因为j> s),如果你从那里走另外的j步,你将在同一个地方回来(因为j是l的倍数).所以j步骤的任何多个都必须保持相同.虽然我们不知道j是先验的,但我们知道它必须存在,我们有效地问"这是j吗?" 每次搬家后,我们都不能错过它. (3认同)
  • 你的证据不是假设你知道你想要找到的周期长度,这样你就可以为野兔选择合适的速度.虽然这将产生一个总是适用于该周期长度的野兔,但不能保证在不同长度的循环中工作(除非您选择速度2). (2认同)
  • @ fd-证明本身并不假设您知道周期长度; 它只是说对于任何循环长度和循环起始位置,有一些具有所需属性的位置j.如果你考虑修改后的tortise/hare算法如何工作,它将开始以速率1和k推进两个指针.在采取j步之后,两个指针将位于j和jk处,这是重合的.您不需要知道j是什么才能到达它.这有意义吗? (2认同)
  • @Nikita Rybak-这是真的.该算法的运行时间与步长成正比,这就是我们通常选择2的原因. (2认同)
  • 详细地说,我可以生成一个证明为什么任何数字都有效:如果考虑相对速度,我们可以将问题简化为兔子绕圈跑,意外地在位置 = 0 处跳过栅栏。只有当相对速度与长度互质并且野兔在单独的陪集上跳跃时,才会发生这种情况(例如,如果野兔从位置 tortoise+1 开始并且相对速度与长度互质,则算法将不起作用)。但兔子必须在同一个陪集中(视觉证明:将非环绕在环上)。量子电子器件。 (2认同)

Sum*_*Das 34

让我们假设该列表的长度不包含循环中s,循环的长度t和比例fast_pointer_speedslow_pointer_speedk.

让两个指针在距j循环开始一段距离处相遇.

所以,距离慢指针移动= s + j.快速指针行进的距离= s + j + m * t(其中m是快速指针完成循环的次数).但是,快速指针也会移动一段距离k * (s + j) (k慢指针的距离).

因此,我们得到k * (s + j) = s + j + m * t.

s + j = (m / k-1)t.

因此,根据上面的等式,慢指针行进的长度是循环长度的整数倍.

为了获得最大效率,(m / k-1) = 1(慢指针不应该多次遍历循环.)

因此, m = k - 1 => k = m + 1

由于m快速指针已经完成循环的次数,m >= 1.为了最大的效率,m = 1.

因此k = 2.

如果我们取值k > 2,那么两个指针必须移动的距离越多.

希望以上解释有所帮助.


use*_*220 6

考虑大小为L的循环,意味着在第k个元素处是循环所在的位置:x k - > x k + 1 - > ... - > x k + L-1 - > x k.假设一个指针以r 1 = 1的速率运行而另一个指针以r 2运行.当第一个指针到达x k时,第二个指针已经在某个元素x k + s的循环中,其中0 <= s <L.在m进一步指针递增后,第一个指针位于x k +(m mod L)并且第二个指针位于x k +((m*r 2 + s)mod L).因此,两个指针碰撞的条件可以表达为满足一致性的m的存在

m = m*r 2 + s(mod L)

这可以通过以下步骤简化

m(1-r 2)= s(mod L)

m(L + 1-r 2)= s(mod L)

这是线性同余的形式.如果s可被gcd(L + 1-r 2,L)整除,则它有一个解.如果gcd(L + 1-r 2,L)= 1,肯定会出现这种情况.如果r 2 = 2则gcd(L + 1-r 2,L)= gcd(L-1,L)= 1并且总是存在解m.

因此,r 2 = 2具有良好的性质,对于任何周期大小L,它满足gcd(L + 1-r 2,L)= 1并因此保证即使两个指针在不同位置开始,指针也将最终发生碰撞.r 2的其他值不具有此属性.


Joh*_*pit 5

如果快指针移动3步而慢指针在1步上移动,则不能保证两个指针在包含偶数个节点的循环中相遇。2但是,如果慢速指针逐步移动,则会议将得到保证。

一般来说,如果兔子一步步移动H,乌龟T一步一步移动,你肯定会在一个循环中相遇 iff H = T + 1

考虑兔子相对于乌龟移动。

  • Hare 相对于乌龟的速度是H - T每次迭代的节点数。
  • 给定一个长度为 的循环N =(H - T) * k,其中k是任何正整数,野兔会跳过每个H - T - 1节点(同样,相对于乌龟),如果乌龟在这些节点中的任何一个,它们就不可能相遇。

  • 保证会议的唯一可能性是何时H - T - 1 = 0

因此,x允许将快指针增加,只要慢指针增加x - 1


Pet*_*ald 5

这是一种直观的非数学方式来理解这一点:

如果快速指针超出链表末尾,则显然不存在循环。

忽略指针位于链表初始非循环部分的初始部分,我们只需要把它们放入循环即可。当慢指针最终到达循环时,快指针在循环中的位置并不重要。

一旦它们都进入循环,它们就会围绕循环运行,但在不同的点上。想象一下,如果它们每次都移动一格。然后他们会绕着自行车转一圈,但保持相同的距离。换句话说,进行相同的循环但异相。现在,通过将快速指针每移动两步,它们就会彼此改变相位;每走一步,彼此的距离就减少一分。快指针将追上慢指针,我们可以检测到循环。

为了证明这是真的,它们会相遇,并且快指针不会以某种方式超越并跳过慢指针,只需手动模拟当快指针落后慢指针三步时会发生什么,然后模拟当快指针落后慢指针时会发生什么落后慢速两步,则当快指针仅落后慢速指针一步时。在每种情况下,它们都在同一节点相遇。任何更大的距离最终都会变成三、二或一的距离。

编辑:如果快指针移动了 3、4 或 5,那么快指针有可能“跳过”慢指针,而不会击中同一个节点。

  • 虽然这可以解释周期检测,但它只解决了“为什么是 2?”的问题。与 1 相比,而不是 3、4、5 等。就这一点而言,虽然这不是一个糟糕的答案,但我认为它实际上并没有回答问题。 (2认同)