假设您在Java中有一个链表结构。它由节点组成:
class Node {
Node next;
// some user data
}
每个节点都指向下一个节点,除了最后一个节点,它的next为空。假设有一种可能性,列表可以包含一个循环-即最后的节点,而不是有一个空值,有一个引用到列表中它之前的一个节点。
最好的写作方式是什么
boolean hasLoop(Node first)
如果给定的节点是带有循环的列表的第一个,则返回true,否则返回false ?你怎么能写出一个常数的空间和合理的时间呢?
下面是一个带有循环的列表的图片:
乌龟和兔子
看看波拉德的算法。这不是完全相同的问题,但也许你会理解其中的逻辑,并将其应用于链表。
(如果你很懒,你可以看看周期检测——看看关于乌龟和兔子的那部分。)
这只需要线性时间和2个额外的指针。
在Java中:
boolean hasLoop( Node first ) {
if ( first == null ) return false;
Node turtle = first;
Node hare = first;
while ( hare.next != null && hare.next.next != null ) {
turtle = turtle.next;
hare = hare.next.next;
if ( turtle == hare ) return true;
}
return false;
}
(大多数解决方案不会同时检查next和next。接下来是null。此外,因为乌龟总是在后面,你不需要检查它是否为空——兔子已经检查过了。)
你可以使用弗洛伊德的周期寻找算法,也被称为乌龟和野兔算法。
其思想是对列表有两个引用,并以不同的速度移动它们。一个向前移动1个节点,另一个向前移动2个节点。
如果链表有一个循环
一定会见面的。
或者
这两个引用(或下一个)
将变为null。
实现算法的Java函数:
boolean hasLoop(Node first) {
if(first == null) // list does not exist..so no loop either
return false;
Node slow, fast; // create two references.
slow = fast = first; // make both refer to the start of the list
while(true) {
slow = slow.next; // 1 hop
if(fast.next != null)
fast = fast.next.next; // 2 hops
else
return false; // next node null => no loop
if(slow == null || fast == null) // if either hits null..no loop
return false;
if(slow == fast) // if the two ever meet...we must have a loop
return true;
}
}