您能解释下面这个在链表中查找循环的片段是如何工作的吗?

时间:2018-05-27 12:43:18

标签: algorithm linked-list floyd-cycle-finding

此代码用于在单个链表中查找循环,并且我从http://blog.ostermiller.org/find-loop-singly-linked-list了解了它,但无法理解为什么代码已经按照编写的方式编写。

这个解决方案由Stephen Ostermiller设计,并由Daniel Martin证实为O(n)。

function boolean hasLoop(Node startNode){
  Node currentNode = startNode;
  Node checkNode = null;
  int since = 0;
  int sinceScale = 2;
  do {
    if (checkNode == currentNode) return true;
    if (since >= sinceScale){
        checkNode = currentNode;
        since = 0;
        sinceScale = 2*sinceScale;
    }
    since++;
  } while (currentNode = currentNode.next());
  return false;
}

最后也提到了这一点:

  

这个解决方案是O(n)因为,因为scale会随着对next()的调用次数而线性增长。一旦sinceScale大于循环的大小,可能需要另外n次调用next()来检测循环。

1 个答案:

答案 0 :(得分:1)

这是布伦特的循环寻找算法。 https://en.wikipedia.org/wiki/Cycle_detection#Brent%27s_algorithm

对于大多数用途,我比Floyd的算法更喜欢它。它确实在O(N)时间内起作用:

  • 需要O(N)步才能将currentNode放入列表的循环部分。
  • 然后,我需要执行O(N)更多步骤,直到since == sinceScalecheckNode设置为currentNode
  • 从那时起,checkNodecurrentNode都在循环中。随着sinceScale变大,checkNode重置的频率会降低。当它足够大时,checkNode将保持不变,直到currentNode一直绕着循环并检测到循环。每次将sinceScale缩放2可确保在O(N)中也发生这种情况。

为了查找链表中的循环,Floyd算法或Brent算法都可以正常工作,但Brent的算法在很多现实生活中更方便到下一个状态的当前状态是昂贵的,并且移动第二个"缓慢"是不切实际的。 Floyd的算法需要的指针。