检测链表中的循环
Detect loop in linked list
首先,我要提一下,我已经查看并了解了最流行的检测链表是否有循环的算法(谈论这个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;
}
我认为您的代码不可能进入死循环。遵循算法的可能控制流程:
- 如果头部为空,return 为假。头部为空现在是不变的。
- 如果头部的下一个节点是它自己,return false。头部被抢先添加到已访问元素的集合中,使这变得微不足道。
- 如果当前节点的下一个节点非空且之前没有被访问过,则将其添加到已访问节点列表中并继续执行。
- 如果当前节点的下一个节点是非空的但之前被访问过,则执行(现在是多余的)检查以确保该节点之前肯定被访问过。在这一点上,我们保证在列表中有一个循环,所以我们 return true.
- 如果当前节点的下一个节点为null,检查之前是否访问过任何null节点。由于我们的不变量,即 head 一开始就不为空,因此无法到达该特定分支。
- 执行(现在是多余的)检查以查看当前节点的下一个节点是否为空。在这一点上,当前元素的下一个元素是 null 是有保证的,因此很容易得出这样的结论:当列表被完全遍历时没有遇到循环并且 return false.[=26= 是安全的]
在任何时候都没有导致程序状态不以某种方式前进的分支,可以肯定地说无限循环不是问题。
您算法的真正问题似乎是它的时间复杂度。确保 java.util.ArrayList 不包含特定值需要完全遍历列表并每次都对每个元素调用 Object#equals。如果您坚决反对使用一种更传统的循环节点 link 检查算法,例如您 link 编辑的算法,我绝对建议您使用 java.util.HashSet 来代替这样的算法.它通过设计提供非常快速的查找元素是否存在于其中。
此外,如果您处在不那么做作的情况下,其中有为节点编写的自定义 equals 和 hashCode 方法,您可能不得不求助于通过包装器 类 和 hashCode 和 equals 来跟踪节点通过 System#identityHashCode 调用代理的方法和 real 节点的引用相等性检查。否则,该算法可能会在实际上不存在循环的情况下确定存在循环。
首先,我要提一下,我已经查看并了解了最流行的检测链表是否有循环的算法(谈论这个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;
}
我认为您的代码不可能进入死循环。遵循算法的可能控制流程:
- 如果头部为空,return 为假。头部为空现在是不变的。
- 如果头部的下一个节点是它自己,return false。头部被抢先添加到已访问元素的集合中,使这变得微不足道。
- 如果当前节点的下一个节点非空且之前没有被访问过,则将其添加到已访问节点列表中并继续执行。
- 如果当前节点的下一个节点是非空的但之前被访问过,则执行(现在是多余的)检查以确保该节点之前肯定被访问过。在这一点上,我们保证在列表中有一个循环,所以我们 return true.
- 如果当前节点的下一个节点为null,检查之前是否访问过任何null节点。由于我们的不变量,即 head 一开始就不为空,因此无法到达该特定分支。
- 执行(现在是多余的)检查以查看当前节点的下一个节点是否为空。在这一点上,当前元素的下一个元素是 null 是有保证的,因此很容易得出这样的结论:当列表被完全遍历时没有遇到循环并且 return false.[=26= 是安全的]
在任何时候都没有导致程序状态不以某种方式前进的分支,可以肯定地说无限循环不是问题。
您算法的真正问题似乎是它的时间复杂度。确保 java.util.ArrayList 不包含特定值需要完全遍历列表并每次都对每个元素调用 Object#equals。如果您坚决反对使用一种更传统的循环节点 link 检查算法,例如您 link 编辑的算法,我绝对建议您使用 java.util.HashSet 来代替这样的算法.它通过设计提供非常快速的查找元素是否存在于其中。
此外,如果您处在不那么做作的情况下,其中有为节点编写的自定义 equals 和 hashCode 方法,您可能不得不求助于通过包装器 类 和 hashCode 和 equals 来跟踪节点通过 System#identityHashCode 调用代理的方法和 real 节点的引用相等性检查。否则,该算法可能会在实际上不存在循环的情况下确定存在循环。