查找单链表是否为循环/循环的有效算法是什么?

时间:2009-07-09 12:27:32

标签: java algorithm data-structures linked-list

如何查找单个链接列表是否为循环/循环?我试图搜索但找不到满意的解决方案。如果可能,您能提供伪代码或Java实现吗?

例如:
135714575,其中第二个5实际上是列表的第三个元素。

12 个答案:

答案 0 :(得分:76)

标准答案是在开始时使用两个迭代器,将第一个迭代器递增一次,将第二个迭代器递增两次。检查它们是否指向同一个对象。然后重复,直到增加两次的那个击中第一个或到达结尾。

此算法在列表中找到任何循环链接,而不仅仅是它是一个完整的圆圈。

伪代码(不是J​​ava,未经测试 - 脱离我的头脑)

bool hasCircle(List l)
{
   Iterator i = l.begin(), j = l.begin();
   while (true) {
      // increment the iterators, if either is at the end, you're done, no circle
      if (i.hasNext())  i = i.next(); else return false;

      // second iterator is travelling twice as fast as first
      if (j.hasNext())  j = j.next(); else return false;
      if (j.hasNext())  j = j.next(); else return false;

      // this should be whatever test shows that the two
      // iterators are pointing at the same place
      if (i.getObject() == j.getObject()) { 
          return true;
      } 
   }
}

答案 1 :(得分:14)

一个名为Floyd's algorithm的简单算法是有两个指针a和b,它们都从链表中的第一个元素开始。然后在每一步增加一次和两次b。重复直到你到达列表的末尾(没有循环),或者a == b(链表包含一个循环)。

另一种算法是Brent's algorithm

答案 2 :(得分:8)

我所知道的三个主要策略:

  1. 开始遍历列表并跟踪您访问过的所有节点(例如,将地址存储在地图中)。您访问的每个新节点,检查您是否已访问过它。如果您已经访问过节点,那么显然有一个循环。如果没有循环,你最终会达到目的。这不是很好,因为存储额外信息的O(N)空间复杂度。

  2. The Tortoise / Hare解决方案。在列表的前面开始两个指针。第一个指针“Tortoise”每次迭代向前移动一个节点。另一个指针,“Hare”每次迭代向前移动两个节点。如果没有循环,野兔和乌龟都将到达列表的末尾。如果有一个循环,野兔会在某个时刻通过乌龟,当发生这种情况时,你知道有一个循环。这是O(1)空间复杂度和非常简单的算法。

  3. 使用算法反转链接列表。如果列表有一个循环,那么在尝试反转它时,你最终会回到列表的开头。如果它没有循环,你将完成倒转并结束。这是O(1)空间复杂度,但算法稍微丑陋。

答案 3 :(得分:4)

我算你的节点再次到达*头。

答案 4 :(得分:2)

答案 5 :(得分:2)

以下方法如何:

按照任何标准算法按升序对链接列表进行排序。 排序前:4-2-6-1-5 排序后:1-2-4-5-6

排序后,检查每个节点数据并与链接节点的数据进行比较,如下所示:

if(currentcode-> data> currentnode-> link-> data) 即circular = true;

在任何比较中,如果“currentnode-> data”中的任何一个对于已排序的链接列表大于“currentcode-> link-> data”,则表示当前节点指向某个先前的节点(即循环);

伙计们,我没有设置测试代码。如果这个概念有效,请告诉我。

答案 6 :(得分:1)

算法是:

  1. 将指针存储到第一个节点
  2. 遍历列表,将每个节点指针与此指针进行比较
  3. 如果遇到NULL指针,那么它不是循环链接列表
  4. 如果您在遍历时遇到第一个节点,那么它将是一个循环链接列表

答案 7 :(得分:0)

从一个节点开始并记录它,然后遍历整个列表,直到到达空指针或您开始使用的节点。

类似的东西:

Node start = list->head;
Node temp = start->next;
bool circular = false;
while(temp != null && temp != start)
{
   if(temp == start)
   {
     circular = true;
     break;
   }
   temp = temp->next;
}
return circular

这是O(n),这是你能用单链表达到的最佳效果(如果我错了,请纠正我)。

或者要查找列表中的任何循环(例如中间),您可以执行以下操作:

Node[] array; // Use a vector or ArrayList to support dynamic insertions
Node temp = list->head;
bool circular = false;
while(temp != null)
{
   if(array.contains(temp) == true)
   {
     circular = true;
     break;
   }
   array.insert(temp);
   temp = temp->next;
}
return circular

由于动态数组的插入时间,这会慢一点。

答案 8 :(得分:0)

@samoz在我的观点中有答案!伪代码丢失。会像

您的列表是您的链接列表

allnodes = hashmap
while yourlist.hasNext()
   node = yourlist.next()
   if(allnodes.contains(node))
      syso "loop found"
      break;
   hashmap.add(node)
抱歉,代码非常伪(最近编写java脚本)

答案 9 :(得分:0)

这是一个不同的解决方案可以复制的好网站。

find loop singly linked list

这是该网站的获胜者

// Best solution
function boolean hasLoop(Node startNode){
  Node slowNode = Node fastNode1 = Node fastNode2 = startNode;
  while (slowNode && fastNode1 = fastNode2.next() && fastNode2 = fastNode1.next()){
    if (slowNode == fastNode1 || slowNode == fastNode2) return true;
    slowNode = slowNode.next();
  }
  return false;
}
  

这个解决方案是“弗洛伊德的   循环寻找算法“已发布   在“非确定性算法”中的   罗伯特W.弗洛伊德在1967年。它也是   被称为“乌龟和野兔”   算法”。

答案 10 :(得分:0)

它永远不会从循环终止,也可以在以下解决方案中完成:

bool hasCircle(List l)
{
   Iterator i = l.begin(), j = l.begin();
   while (true) {
      // increment the iterators, if either is at the end, you're done, no circle
      if (i.hasNext())  i = i.next(); else return false;

      // second iterator is travelling twice as fast as first
      if (j.hasNext())  j = j.next(); else return false;
      if (j.hasNext())  j = j.next(); else return false;

      // this should be whatever test shows that the two
      // iterators are pointing at the same place
      if (i.getObject() == j.getObject()) { 
          return true;
      } 
      if(i.next()==j)
          break;
    }
}

答案 11 :(得分:0)

试试这个

/* Link list Node */

struct Node {     int数据;     struct Node * next; };

/ *如果给定链接,则此函数返回true    list是循环的,否则为false。 * / bool isCircular(struct Node * head) {     //空链表是循环的     if(head == NULL)        return true;

// Next of head
struct Node *node = head->next;

// This loop would stope in both cases (1) If
// Circular (2) Not circular
while (node != NULL && node != head)
   node = node->next;

// If loop stopped because of circular
// condition
return (node == head);

}