此代码用于在单个链表中查找循环,并且我从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()来检测循环。
答案 0 :(得分:1)
这是布伦特的循环寻找算法。 https://en.wikipedia.org/wiki/Cycle_detection#Brent%27s_algorithm
对于大多数用途,我比Floyd的算法更喜欢它。它确实在O(N)时间内起作用:
currentNode
放入列表的循环部分。since == sinceScale
,checkNode
设置为currentNode
checkNode
和currentNode
都在循环中。随着sinceScale
变大,checkNode
重置的频率会降低。当它足够大时,checkNode
将保持不变,直到currentNode
一直绕着循环并检测到循环。每次将sinceScale
缩放2可确保在O(N)中也发生这种情况。为了查找链表中的循环,Floyd算法或Brent算法都可以正常工作,但Brent的算法在很多现实生活中更方便到下一个状态的当前状态是昂贵的,并且移动第二个"缓慢"是不切实际的。 Floyd的算法需要的指针。