【问题标题】:How to find loop-node of a linkedlist with a loop?如何找到带有循环的链表的循环节点?
【发布时间】:2013-08-27 19:53:28
【问题描述】:

这个问题与查找 2 个链表的交集有点不同。

考虑一个带循环的链表:A - B - C - D - E - F - C

如果节点A是函数的输入,那么它应该返回C

由于我不知道该怎么称呼C,所以我使用了一个术语循环节点C,如问题中所示。虽然 O(n2) 项看起来很明显,但有没有办法找到复杂度较低的循环节点?

不允许使用 O(n) 的哈希表/额外空间。

【问题讨论】:

  • 其实我应该在不使用哈希的情况下提到它。谢谢指出
  • @MitchWheat 我听说链表问题有同样的限制。这使问题变得更加困难。
  • StackOverFlow 上有很多关于这个问题的好答案。这是我最喜欢的:stackoverflow.com/questions/2663115/…
  • 这不是“循环检测”,问题是要找到循环的点,有 2 个节点指向它。

标签: java algorithm data-structures linked-list singly-linked-list


【解决方案1】:

有一种使用两个指针的简单方法。第一个指针以慢指针的速度递增一秒,第二个指针以慢速指针的速度递增。

所以在你的情况下,链表实际上是A->B->C->D->E->F->C,这意味着 F 再次指向 C。所以方法如下所示

1.继续增加两个pointers直到它们匹配。所以在上面的例子中我们会有这些步骤

慢指针:A B C D E
快速指针:A C E C E

所以我们停在E处,这表明有一个循环。现在我们需要找到循环节点。

现在从 E 将慢速指针移动到链表的开头并创建一个指向 E 并递增 1 的新指针。这两个指针相遇的点实际上是循环节点。所以在我们的例子中

指针从头开始:A B C 新指针:E F C

如您所见,他们在 C 处相遇,我们在链表中找到了循环节点。

更新: 对于这种方法的数学证明,请参考这个精彩的问题并查看@Jim Lewis 的答案以及答案下方的所有 cmets。 Explain how finding cycle start node in cycle linked list work?

【讨论】:

  • 这种算法被称为“弗洛伊德的循环寻找算法”或简称为“龟兔赛跑”。
  • 谢谢,这在表面上非常合乎逻辑。假设 E 是链表的结尾,我们已经用慢速指针覆盖了一半的距离。
  • @JavaDeveloper 我添加了数学证明的链接。请务必阅读。
【解决方案2】:

Floyd's cycle-finding algorithm 是最简单的一个,通常是“规范答案”,因为这是每个人在大学里都学过的(也许是因为它简单、优雅且富有洞察力)。

还经常声称运行时不可改进,但事实并非如此 - 大 Oh 可能不可改进,但这只能告诉我们在最坏情况下的渐近行为。 Brent's algorithm 在实践中更快,同时仍使用恒定数量的空间。还有更多的算法,例如 Gosper 的循环检测或 Sedgewick、Szymanski 和 Yao 的算法或 k-stacks algorithm,它们都使用一定的(低,但理论上是非恒定的)空间量。实际上,对于链表的任何实际实现,空间量仍然是恒定的,因为您的指针将是固定大小的。例如,对于 32 位指针,Gosper 的循环检测将使用 33 个字的空间(可能还有几个额外的字,具体取决于您要计算的内容)。

弗洛伊德的算法很好,但不一定是答案(tm)。需要做出选择和权衡。

【讨论】:

    猜你喜欢
    • 2011-02-25
    • 2015-08-08
    • 1970-01-01
    • 2012-07-20
    • 2012-06-08
    • 2019-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多