检测链表中的循环

时间:2016-12-11 18:44:55

标签: java linked-list infinite-loop circular-reference

首先,我应该提一下,我已经检查并理解了最流行的算法,用于检测链接列表是否有循环(讨论此How to detect a loop in a linked list?

然而,我尝试实现自己的想法,它似乎适用于我的示例 - 但它在http://leetcode.com/的执行时间失败,这使我认为它可以进入无限循环。

我的问题是:我需要知道我的代码是否正确,或者在某些情况下它确实会进入无限循环。

这是单链表的定义。

class ListNode {
   int val;
   ListNode next;
   ListNode(int x) {
       val = x;
       next = null;
   }
}

这是功能:

public boolean hasCycle(ListNode head) {
    //create a list that will contain the elements that have been visited
    List<ListNode> ref = new ArrayList<ListNode>();

    ListNode element = head;
    //add the first element to the list
    ref.add(head);

    boolean contains = false;

    //the special case when the linkedlist is empty
    if(head==null)
        return false;
    while(contains == false){
        if(element.next!=null && !ref.contains(element.next)){
            ref.add(element.next);
            element = element.next;
            contains = false;
        }
        else if (ref.contains(element.next))
            contains = true;
        else if(element.next == null)
            return false;
    }
    return contains;
}

1 个答案:

答案 0 :(得分:0)

我认为您的代码无法进入无限循环。遵循算法的可能控制流程:

  • 如果head为null,则返回false。头为空现在是一个不变量。
  • 如果head的下一个节点本身,则返回false。头部被预先添加到被访问元素的集合中,这使得这很简单。
  • 如果当前节点的下一个节点非空并且以前没有访问过,请将其添加到访问节点列表中并继续执行。
  • 如果当前节点的下一个节点非空但之前已被访问过,请执行(现在多余的)检查以确保该节点先前已被访问过。此时我们保证在列表中有一个循环,所以我们返回true。
  • 如果当前节点的下一个节点为空,请检查先前是否已访问过任何空节点。这个特殊的分支是无法达到的,这要归功于我们的头部不是一开始就是空的。
  • 执行(现在多余的)检查以查看当前节点的下一个节点是否为空。此时,当前元素的下一个元素为空是有保证的,因此可以得出结论,当列表完全遍历时没有遇到循环并且返回false是安全的。

在任何时候都没有一个分支导致程序状态没有以某种方式前进,因此可以安全地说无限循环不是问题。

您的算法的真正问题似乎是它的时间复杂性。确保java.util.ArrayList不包含特定值需要完全遍历列表并每次在每个元素上调用Object#equals。如果你没有使用像你所链接的更传统的循环节点链接检查算法之一,我肯定建议使用java.util.HashSet代替这样的东西。它通过设计提供非常快速的查找,以确定元素是否存在于其中。

此外,如果您处于一种不太习惯的情况下,其中存在为节点编写的自定义equals和hashCode方法,则可能不得不求助于通过使用hashCode和equals方法通过系统代理的包装类来跟踪节点。 #identityHashCode对真实节点的调用和引用相等性检查。否则,当一个人确实没有循环时,算法可能会确定存在一个循环。