如何检测链表中的循环?
假设您在Java中有一个链表结构。 它由节点组成:
class Node {Node next;// some user data
}
每个节点都指向下一个节点,但最后一个节点除外,后者的下一个为空。 假设列表有可能包含一个循环-即最终Node(而不是null)具有对列表中位于其之前的节点之一的引用。
最好的写作方式是什么
boolean hasLoop(Node first)
如果给定的Node是带有循环的列表的第一个,则返回true
否则返回false
? 您怎么写才能占用恒定的空间和合理的时间?
这是带有循环的列表的外观图:
#1楼
您甚至可以在恒定的O(1)时间内执行此操作(尽管它不是非常快或高效):您的计算机内存可以容纳的节点数量有限,例如N条记录。 如果遍历多于N条记录,则有一个循环。
#2楼
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;}
#3楼
比弗洛伊德算法更好
理查德·布伦特(Richard Brent)描述了一种替代的周期检测算法 ,该算法与野兔和乌龟[弗洛伊德的周期]非常相似,不同之处在于,此处的慢速节点不移动,但后来“传送”到固定位置的快速节点的位置。间隔。
此处提供了描述: http : //www.siafoo.net/algorithm/11布伦特声称他的算法比弗洛伊德循环算法快24%至36%。 O(n)时间复杂度,O(1)空间复杂度。
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;
}
#4楼
算法
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)
#5楼
我可能来晚了又很新来处理这个线程。 但是还是
为什么不能将节点和指向的“下一个”节点的地址存储在表中
如果我们可以这样制表
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)
因此,形成了一个循环。
#6楼
这种方法有空间开销,但是实现起来比较简单:
可以通过将节点存储在Map中来标识循环。 并且在放置节点之前; 检查节点是否已经存在。 如果节点已存在于映射中,则意味着链接列表具有循环。
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; }
#7楼
乌龟和野兔
看一下Pollard的rho算法 。 这不是完全相同的问题,但是也许您会从中了解逻辑,并将其应用于链接列表。
(如果您很懒,可以只检查周期检测 -检查有关乌龟和野兔的部分。)
这仅需要线性时间和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.next
是否为空。而且,由于乌龟始终在后面,因此您不必将其检查为空,因为兔子已经这样做了。)
#8楼
以下可能不是最佳方法-它是O(n ^ 2)。 但是,它应该有助于完成工作(最终)。
count_of_elements_so_far = 0;
for (each element in linked list)
{search for current element in first <count_of_elements_so_far>if found, then you have a loopelse,count_of_elements_so_far++;
}
#9楼
您可以利用弗洛伊德(Floyd)的循环查找算法 ,也称为草龟和野兔算法 。
想法是要有两个引用列表,并以不同的速度移动它们。 将一个节点向前移动1
节点,将另一个节点向前移动2
节点。
- 如果链表有循环,它们肯定会碰面。
- 其他两个引用(或它们的
next
)将变为null
。
实现该算法的Java函数:
boolean hasLoop(Node first) {if(first == null) // list does not exist..so no loop eitherreturn false;Node slow, fast; // create two references.slow = fast = first; // make both refer to the start of the listwhile(true) {slow = slow.next; // 1 hopif(fast.next != null)fast = fast.next.next; // 2 hopselsereturn false; // next node null => no loopif(slow == null || fast == null) // if either hits null..no loopreturn false;if(slow == fast) // if the two ever meet...we must have a loopreturn true;}
}
#10楼
我看不到有什么方法可以使它花费固定的时间或空间,两者都会随着列表的大小而增加。
我将使用一个IdentityHashMap(假设还没有IdentityHashSet)并将每个Node存储到地图中。 在存储节点之前,您将在其上调用containsKey。 如果该节点已经存在,则有一个循环。
ItentityHashMap使用==代替.equals,以便您检查对象在内存中的位置,而不是对象是否具有相同的内容。
#11楼
我暂时更改列表后,Turtle and Rabbit的替代解决方案不太好:
想法是遍历列表,并在执行时将其反转。 然后,当您第一次到达已被访问的节点时,其下一个指针将指向“向后”,从而导致迭代再次朝着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);
}
#12楼
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;
}
原谅我的无知(我对Java和编程还是相当陌生的),但是为什么上面的方法不起作用?
我猜这不能解决恒定的空间问题...但是至少可以在合理的时间内到达那里,对吗? 它只会占用链接列表的空间加上具有n个元素的集合的空间(其中n是链接列表中的元素数,或者直到到达循环为止的元素数)。 对于时间,我认为最坏情况的分析建议使用O(nlog(n))。 对contains()的SortedSet查找为log(n)(请检查javadoc,但我很确定TreeSet的基础结构是TreeMap,而后者又是一棵红黑树),在最坏的情况下(没有循环,或在最后循环),则必须进行n次查找。
#13楼
如果允许我们嵌入类Node
,那么我将在下面实现它的同时解决该问题。 hasLoop()
以O(n)时间运行,并且仅占用counter
的空间。 这似乎是一个合适的解决方案吗? 还是有一种无需嵌入Node
? (显然,在实际的实现中,会有更多的方法,例如RemoveNode(Node n)
等)
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;....}}
#14楼
用户unicornaddict在上面有一个不错的算法,但是不幸的是,它包含一个错误循环,用于奇数长度> = 3的非循环列表。问题在于, fast
可能会在列表末尾之前“卡住”,而slow
赶上它,并且(错误地)检测到循环。
这是更正后的算法。
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;elsefast = 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;}
}
#15楼
这是对快速/慢速解决方案的改进,可以正确处理奇数长度列表并提高清晰度。
boolean hasLoop(Node first) {Node slow = first;Node fast = first;while(fast != null && fast.next != null) {slow = slow.next; // 1 hopfast = fast.next.next; // 2 hops if(slow == fast) // fast caught up to slow, so there is a loopreturn true;}return false; // fast reached null, so the list terminates
}
#16楼
可以用最简单的方法之一来检测链表中的循环,这会导致使用哈希图导致O(N)复杂性,或者使用基于排序的方法导致O(NlogN)复杂性。
从头开始遍历列表时,请创建一个排序的地址列表。 当您插入新地址时,请检查该地址是否已存在于排序列表中,这会增加O(logN)的复杂度。
#17楼
这是我的可运行代码。
我所做的是通过使用三个跟踪链接的临时节点(空间复杂度O(1)
)来检查链接列表。
这样做的有趣之处在于,它有助于检测链表中的循环,因为在前进过程中,您不希望返回起始点(根节点),并且其中一个临时节点应该变为空,除非您这样做。有一个周期,这意味着它指向根节点。
该算法的时间复杂度为O(n)
,空间复杂度为O(1)
。
这是链表的类节点:
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));}
}
#18楼
// 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 aheadwhile (true) {// if the faster pointer encounters a NULL elementif (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 listelse if (slower == faster || slower == faster.next)return true;else {// advance the pointersslower = slower.next;faster = faster.next.next;}}
}
#19楼
该代码经过了优化,其结果生成速度比选择最佳答案的速度更快。此代码避免了追逐向前和向后节点指针的漫长过程,如果我们遵循“最佳选择”,则会在以下情况下发生回答''方法。仔细阅读下面的内容,您将了解我要说的内容。然后通过下面的给定方法查看问题并测量否。 找到答案的步骤。
1-> 2-> 9-> 3 ^ -------- ^
这是代码:
boolean loop(node *head)
{node *back=head;node *front=head;while(front && front->next){front=front->next->next;if(back==front)return true;elseback=back->next;}
return false
}
#20楼
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;
}
使用上述功能可以检测Java中链表中的循环。
#21楼
这是我在Java中的解决方案
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;
}
#22楼
您也可以按照上述答案中的建议使用Floyd的陆龟算法。
该算法可以检查单链表是否具有闭合循环。 这可以通过用两个将以不同速度移动的指针迭代一个列表来实现。 这样,如果存在一个周期,则两个指针将在将来的某个时刻相遇。
请随时查看我在链接列表数据结构上的博客文章 ,其中还包括一个代码片段,其中包含上述Java语言算法的实现。
问候,
安德里亚斯(@xnorcode)
#23楼
这是检测周期的解决方案。
public boolean hasCycle(ListNode head) {ListNode slow =head;ListNode fast =head;while(fast!=null && fast.next!=null){slow = slow.next; // slow pointer only one hopfast = 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 }
#24楼
在这种情况下,文本材料无处不在。 我只是想发布一个图表表示形式,这确实帮助我理解了这个概念。
当快慢点在p点相遇时,
快速行进的距离= a + b + c + b = a + 2b + c
慢速行驶的距离= a + b
因为快是慢的2倍。 所以a + 2b + c = 2(a + b) ,则得到a = c 。
因此,当另一个慢速指针再次从头运行到q时,快速指针将从p到q运行,因此它们在点q处相遇。
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 equalsdist from head to start of loop*/if (fast == slow){ //loop foundslow = head;while(slow != fast){slow = slow.next;fast = fast.next;}return slow;} }return null;
}
#25楼
//链表查找循环功能
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;
}
如何检测链表中的循环?相关推荐
- python代码检测链表中的环并删除环
python代码检测链表中的环并删除环 在计算机科学中,链表是数据元素的线性集合,其顺序不是由它们在内存中的物理位置决定的.相反,每个元素指向下一个元素.它是一种数据结构,由一组节点组成,这些节点共同 ...
- 如何检测链表中是存在循环
链表在面试中出现的频率很高,有的比较正常,考链表的常规操作,主要看基本功是否扎实,有些就比较难,难在思维的改变和是否能够想到对应的点.这里出现的是其中一个题目,我称之为有环链表问题.也就是从判断一个单 ...
- 写出一段代码将链表中的两个节点位置互换位置_干货||链表的技巧和算法总结...
链表的操作总结 链表反转 这是一个简单的链表操作问题,在leetcode上面有52.7%的通过率,难度是简单.但是还是想在这里基于python做一下总结,顺便总结一下链表的各种操作. 首先先看一下 ...
- 数据结构初阶(4)(OJ练习【判断链表中是否有环、返回链表入口点、删除链表中的所有重复出现的元素】、双向链表LinkedList【注意事项、构造方法、常用方法、模拟实现、遍历方法、顺序表和链表的区别)
接上次博客:数据结构初阶(3)(链表:链表的基本概念.链表的类型.单向不带头非循环链表的实现.链表的相关OJ练习.链表的优缺点 )_di-Dora的博客-CSDN博客 目录 OJ练习 双向链表--Li ...
- 判断单链表中的元素是否递增_检测单链表中是否有环(C语言)
检测单链表中是否有环(C语言) 方法:双指针法思路 使用两个指针,两个初始时都指向链表的头结点,然后one指针一次加一,另一个two指针一次加二. 在链表有环时,two指针与one指针相等就说明有环. ...
- 【问题描述】在带头结点单链表中查找最大值,将其值与最后一个元素交换,输出交换后的单链表各元素。【输入形式】循环输入若干个整数,以字母结束输入,建立带头结点的单链表。【输出形式】输出最
[问题描述] 在带头结点单链表中查找最大值,将其值与最后一个元素交换,输出交换后的单链表各元素. [输入形式] 循环输入若干个整数,以字母结束输入,建立带头结点的单链表. [输出形式] 输出最大值与最 ...
- lua检测表中是否有某个值_Lua基础知识总结(入职面试题)
2019年8月刚入职新公司时,因为之前的项目都没有使用lua的经验,所以jojo老大出了一份题让我想尽办法找出答案,当时对于一个无经验的小菜鸟来说,属实费了不少功夫,如今分享出来,希望能对刚使用lua ...
- 单链表中倒数第K个结点
单链表中倒数第K个结点 链表结点定义如下: typedef int ElemType;typedef struct Node {ElemType data; struct Node *next; }H ...
- LeetCode实战:删除链表中的节点
背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 Write a fun ...
最新文章
- Springboot 2.返回cookies信息的get接口开发 和 带cookis去请求
- Anaconda(miniconda)安装及使用--转
- 利用Virtual Audio Cable实现系统声音录制
- OSI七层与TCP/IP四/五层网络架构
- 减治法解决八枚硬币问题/假币问题(JAVA)----二分,三分,不知轻重的情况
- JavaScript中引号的多重嵌套
- MFC 单文档应用程序 dialog 变量传递
- 内容 超链接_Word高效办公:自动创建带超链接的内容目录和图表目录
- MIPI-DSI 三种 Video Mode 理解
- UOJ 30 【CF Round #278】Tourists
- qlv转mp4失败 解决方法
- 什么软件测试苹果手机循环电池,如何检查iPhone电池的电池循环次数,看完你就明白了...
- NISP一级模拟题(一、二)
- 【图像视觉基础】浅谈CCD和CMOS相机的联系和区别
- android中listView下拉刷新
- 双目立体匹配算法SGBM
- 《财富自由之路》读书笔记
- 【它山之玉】博士三年发表论文回顾—科学网马臻
- Excel工作表单保护密码忘记如何找回
- 超声波脉冲发生器可调电源设计
热门文章
- 走过路过不要错过,面了六轮才拿到阿里Android研发岗的Offer,确定不来看看?
- 从URL输入到页面展现,过程中发生了什么?
- H5 App开发工具 WeX5
- python中标识符下划线用作开头_python python中那些双下划线开头的那些函数都是干啥用用的...
- Android之自定义控件显示点赞用户并通过用户名称进入该用户主页的功能
- (0025)iOS 开发之Xcode常用快捷键
- php excel parser pro v4.2,php中使用ExcelFileParser处理excel获得数据(可作批量导入到数据库使用)...
- arcgis python 新建图层组_分享Arcgis中62个常用技巧系列一(前20个技巧)
- 【转载】flash时间轴中变量的作用域
- 大北农集团被指控授意窃取商业秘密