Java 如何检测链表中的循环?
假设您在Java中有一个链表结构。它由节点组成:Java 如何检测链表中的循环?,java,algorithm,data-structures,linked-list,Java,Algorithm,Data Structures,Linked List,假设您在Java中有一个链表结构。它由节点组成: class Node { Node next; // some user data } 每个节点都指向下一个节点,但最后一个节点除外,该节点的next值为null。假设列表可能包含一个循环,也就是说,最后一个节点不是空的,而是对列表中位于它之前的一个节点的引用 最好的写作方式是什么 boolean hasLoop(Node first) 如果给定节点是带有循环的列表的第一个节点,则返回true,否则返回false?你怎么能写得
class Node {
Node next;
// some user data
}
每个节点都指向下一个节点,但最后一个节点除外,该节点的next值为null。假设列表可能包含一个循环,也就是说,最后一个节点不是空的,而是对列表中位于它之前的一个节点的引用
最好的写作方式是什么
boolean hasLoop(Node first)
如果给定节点是带有循环的列表的第一个节点,则返回true
,否则返回false
?你怎么能写得如此之快,以至于它需要恒定的空间和合理的时间
下面是一张带有循环的列表的图片:
看一看。这不是完全相同的问题,但也许你会从中理解逻辑,并将其应用于链表
(如果你很懒,你可以看看——看看关于乌龟和兔子的部分。)
这只需要线性时间和两个额外的指针
在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.next
是否为空。此外,由于海龟总是在后面,您不必检查它是否为空——兔子已经检查过了。)以下方法可能不是最好的方法——它是O(n^2)。然而,它应该有助于完成工作(最终)
count\u of_elements\u sou\u far=0;
for(链表中的每个元素)
{
首先搜索当前元素
如果找到,则有一个循环
否则,计算元素的数量;
}
您可以使用,也称为龟兔算法。这样做的目的是让列表中有两个引用,并以不同的速度移动它们。通过
1
节点向前移动一个,通过2
节点向前移动另一个
- 如果链表有一个循环,它们 一定会见面的
- 要不然你们两个
两个引用(或它们的
下一个
) 将变为
null
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;
}
}
我看不出有任何方法可以使这需要固定的时间或空间,两者都会随着列表的大小而增加 我将使用IdentityHashMap(假设还没有IdentityHashSet),并将每个节点存储到map中。在存储节点之前,您需要对其调用containsKey。如果节点已经存在,则有一个循环
ItentityHashMap使用==而不是.equals,这样您就可以检查对象在内存中的位置,而不是它是否具有相同的内容。海龟和兔子的替代解决方案,没有我临时更改列表那么好: 这样做的目的是浏览列表,并在执行时将其反转。然后,当您第一次到达一个已经访问过的节点时,它的下一个指针将指向“向后”,导致迭代再次向
first
前进,并在此处终止
Node prev = null;
Node cur = first;
while (cur != null) {
Node next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
boolean hasCycle = prev == first && first != null && first.next != null;
// reconstruct the list
cur = prev;
prev = null;
while (cur != null) {
Node next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return hasCycle;
测试代码:
static void assertSameOrder(Node[] nodes) {
for (int i = 0; i < nodes.length - 1; i++) {
assert nodes[i].next == nodes[i + 1];
}
}
public static void main(String[] args) {
Node[] nodes = new Node[100];
for (int i = 0; i < nodes.length; i++) {
nodes[i] = new Node();
}
for (int i = 0; i < nodes.length - 1; i++) {
nodes[i].next = nodes[i + 1];
}
Node first = nodes[0];
Node max = nodes[nodes.length - 1];
max.next = null;
assert !hasCycle(first);
assertSameOrder(nodes);
max.next = first;
assert hasCycle(first);
assertSameOrder(nodes);
max.next = max;
assert hasCycle(first);
assertSameOrder(nodes);
max.next = nodes[50];
assert hasCycle(first);
assertSameOrder(nodes);
}
静态void assertSameOrder(节点[]节点){
对于(int i=0;i
公共布尔hasLoop(节点开始){
树集=新树集();
节点查找at=开始;
while(lookingAt.peek()!=null){
lookingAt=lookingAt.next;
if(set.contains(lookingAt){
返回false;
}否则{
设置、放置(查找);
}
返回true;
}
//在我们的节点类中:
公共节点peek(){
把这个还给我,下一个;
}
请原谅我的无知(我对Java和编程还是相当陌生),但为什么上面的方法行不通呢
我想这并不能解决常量空间的问题……但它至少能在一段合理的时间内到达,对吗?它只会占用链表的空间加上一个包含n个元素的集合的空间(其中n是链表中的元素数,或者是到达循环之前的元素数)我认为,最坏情况下的分析会建议O(nlog(n)).SortedSet查找contains()是log(n)(检查javadoc,但我非常确定TreeSet的底层结构是TreeMap,而TreeMap又是一棵红黑树),在最坏情况下(没有循环,或者循环在最末端),它将必须进行n次查找。如果允许我们嵌入类
节点
,我将在下面实现时解决该问题。hasLoop()
在O(n)中运行时间,只占用计数器的空间。这似乎是一个合适的解决方案吗?或者有没有一种方法可以不嵌入节点?(显然,在实际实现中会有更多的方法,如RemoveNode(Node n)
,等等)
公共类LinkedNodeList{
节点优先;
整数计数;
LinkedNodeList(){
第一个=空;
计数=0;
}
LinkedNodeList(节点n){
如果(n.next!=null){
抛出新错误(“必须从单个节点开始!”);
}否则{
第一个=n;
计数=1;
}
}
公共void addNode(节点n){
节点查找at=第一;
while(lookingAt.next!=null){
lookingAt=lookingAt.next;
}
lookingAt.next=n;
计数++;
}
公共布尔hasLoop(){
int计数器=0;
节点查找at=第一;
while(lookingAt.next!=null){
static void assertSameOrder(Node[] nodes) {
for (int i = 0; i < nodes.length - 1; i++) {
assert nodes[i].next == nodes[i + 1];
}
}
public static void main(String[] args) {
Node[] nodes = new Node[100];
for (int i = 0; i < nodes.length; i++) {
nodes[i] = new Node();
}
for (int i = 0; i < nodes.length - 1; i++) {
nodes[i].next = nodes[i + 1];
}
Node first = nodes[0];
Node max = nodes[nodes.length - 1];
max.next = null;
assert !hasCycle(first);
assertSameOrder(nodes);
max.next = first;
assert hasCycle(first);
assertSameOrder(nodes);
max.next = max;
assert hasCycle(first);
assertSameOrder(nodes);
max.next = nodes[50];
assert hasCycle(first);
assertSameOrder(nodes);
}
public boolean hasLoop(Node start){
TreeSet<Node> set = new TreeSet<Node>();
Node lookingAt = start;
while (lookingAt.peek() != null){
lookingAt = lookingAt.next;
if (set.contains(lookingAt){
return false;
} else {
set.put(lookingAt);
}
return true;
}
// Inside our Node class:
public Node peek(){
return this.next;
}
public class LinkedNodeList {
Node first;
Int count;
LinkedNodeList(){
first = null;
count = 0;
}
LinkedNodeList(Node n){
if (n.next != null){
throw new error("must start with single node!");
} else {
first = n;
count = 1;
}
}
public void addNode(Node n){
Node lookingAt = first;
while(lookingAt.next != null){
lookingAt = lookingAt.next;
}
lookingAt.next = n;
count++;
}
public boolean hasLoop(){
int counter = 0;
Node lookingAt = first;
while(lookingAt.next != null){
counter++;
if (count < counter){
return false;
} else {
lookingAt = lookingAt.next;
}
}
return true;
}
private class Node{
Node next;
....
}
}
static 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 = null;
else
fast = fast.next.next; // 2 hops.
if(fast == null) // if fast hits null..no loop.
return false;
if(slow == fast) // if the two ever meet...we must have a loop.
return true;
}
}
boolean hasLoop(Node first) {
Node slow = first;
Node fast = first;
while(fast != null && fast.next != null) {
slow = slow.next; // 1 hop
fast = fast.next.next; // 2 hops
if(slow == fast) // fast caught up to slow, so there is a loop
return true;
}
return false; // fast reached null, so the list terminates
}
public boolean isCircular() {
if (head == null)
return false;
Node temp1 = head;
Node temp2 = head;
try {
while (temp2.next != null) {
temp2 = temp2.next.next.next;
temp1 = temp1.next;
if (temp1 == temp2 || temp1 == temp2.next)
return true;
}
} catch (NullPointerException ex) {
return false;
}
return false;
}
public static boolean hasLoop(Node root){
if(root == null) return false;
Node slow = root, fast = root;
int taken = 0, limit = 2;
while (fast.next != null) {
fast = fast.next;
taken++;
if(slow == fast) return true;
if(taken == limit){
taken = 0;
limit <<= 1; // equivalent to limit *= 2;
slow = fast; // teleporting the turtle (to the hare's position)
}
}
return false;
}
public static boolean hasCycle (LinkedList<Node> list)
{
HashSet<Node> visited = new HashSet<Node>();
for (Node n : list)
{
visited.add(n);
if (visited.contains(n.next))
{
return true;
}
}
return false;
}
Time ~ O(n)
Space ~ O(n)
node present: (present node addr) (next node address)
node 1: addr1: 0x100 addr2: 0x200 ( no present node address till this point had 0x200)
node 2: addr2: 0x200 addr3: 0x300 ( no present node address till this point had 0x300)
node 3: addr3: 0x300 addr4: 0x400 ( no present node address till this point had 0x400)
node 4: addr4: 0x400 addr5: 0x500 ( no present node address till this point had 0x500)
node 5: addr5: 0x500 addr6: 0x600 ( no present node address till this point had 0x600)
node 6: addr6: 0x600 addr4: 0x400 ( ONE present node address till this point had 0x400)
public boolean loopDetector(Node<E> first) {
Node<E> t = first;
Map<Node<E>, Node<E>> map = new IdentityHashMap<Node<E>, Node<E>>();
while (t != null) {
if (map.containsKey(t)) {
System.out.println(" duplicate Node is --" + t
+ " having value :" + t.data);
return true;
} else {
map.put(t, t);
}
t = t.next;
}
return false;
}
public class LinkedNode{
public LinkedNode next;
}
public static boolean checkLoopInLinkedList(LinkedNode root){
if (root == null || root.next == null) return false;
LinkedNode current1 = root, current2 = root.next, current3 = root.next.next;
root.next = null;
current2.next = current1;
while(current3 != null){
if(current3 == root) return true;
current1 = current2;
current2 = current3;
current3 = current3.next;
current2.next = current1;
}
return false;
}
public class questions{
public static void main(String [] args){
LinkedNode n1 = new LinkedNode();
LinkedNode n2 = new LinkedNode();
LinkedNode n3 = new LinkedNode();
n1.next = n2;
n2.next = n3;
n3.next = n2;
System.out.print(checkLoopInLinkedList(n1));
}
}
// To detect whether a circular loop exists in a linked list
public boolean findCircularLoop() {
Node slower, faster;
slower = head;
faster = head.next; // start faster one node ahead
while (true) {
// if the faster pointer encounters a NULL element
if (faster == null || faster.next == null)
return false;
// if faster pointer ever equals slower or faster's next
// pointer is ever equal to slower then it's a circular list
else if (slower == faster || slower == faster.next)
return true;
else {
// advance the pointers
slower = slower.next;
faster = faster.next.next;
}
}
}
boolean loop(node *head)
{
node *back=head;
node *front=head;
while(front && front->next)
{
front=front->next->next;
if(back==front)
return true;
else
back=back->next;
}
return false
}
boolean hasCycle(Node head) {
boolean dec = false;
Node first = head;
Node sec = head;
while(first != null && sec != null)
{
first = first.next;
sec = sec.next.next;
if(first == sec )
{
dec = true;
break;
}
}
return dec;
}
boolean detectLoop(Node head){
Node fastRunner = head;
Node slowRunner = head;
while(fastRunner != null && slowRunner !=null && fastRunner.next != null){
fastRunner = fastRunner.next.next;
slowRunner = slowRunner.next;
if(fastRunner == slowRunner){
return true;
}
}
return false;
}
public boolean hasCycle(ListNode head) {
ListNode slow =head;
ListNode fast =head;
while(fast!=null && fast.next!=null){
slow = slow.next; // slow pointer only one hop
fast = fast.next.next; // fast pointer two hops
if(slow == fast) return true; // retrun true if fast meet slow pointer
}
return false; // return false if fast pointer stop at end
}
public ListNode detectCycle(ListNode head) {
if(head == null || head.next==null)
return null;
ListNode slow = head;
ListNode fast = head;
while (fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
/*
if the 2 pointers meet, then the
dist from the meeting pt to start of loop
equals
dist from head to start of loop
*/
if (fast == slow){ //loop found
slow = head;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
int findLoop(struct Node* head)
{
struct Node* slow = head, *fast = head;
while(slow && fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return 1;
}
return 0;
}