链表(篇6)Floyd破圈法以及其改进方法检查删除链表中的环
来源:互联网 发布:英语词典哪个好 知乎 编辑:程序博客网 时间:2024/06/03 20:43
检查给定的链表是否包含环,如果环存在,则删除环并返回true。如果列表不包含环,则返回false。下图显示了一个带有环的链表。必须将以下列表更改为1-> 2-> 3-> 4-> 5-> NULL。
思路:
要删除循环,我们需要做的是获取指向循环的最后一个节点的指针。例如,上图中值为5的节点。一旦我们有指向最后一个节点的指针,我们可以使这个节点的下一个为NULL。可以使用Floyd循环检测算法来检测和删除循环。在Floyd算法中,慢速和快速指针在循环节点处相遇。可以利用这个循环节点来去除循环。
Floyd的算法
如果有限状态机、迭代函数或者链表上存在环,那么在某个环上以不同速度前进的2个指针必定会在某个时刻相遇。同时显然地,如果从同一个起点(即使这个起点不在某个环上)同时开始以不同速度前进的2个指针最终相遇,那么可以判定存在一个环,且可以求出2者相遇处所在的环的起点与长度。
解法1:
在Floyd算法中采用两个不同速度的ptr1,ptr2,它们相遇时肯定处于环中,如上图中的2,3,4,5 中的某一位置。使用一个新节点p从1开始,ptr2围着环2-3-4-5转一圈,如果ptr2碰到了p则肯定是p在2的位置ptr2恰好在5的位置。
代码:
// 检测并删除链表中的环class LinkedList { static Node head; static class Node { int data; Node next; Node(int d) { data = d; next = null; } } // Floyd方法检测是否有环 //其中fast每次向前移动2个节点,slow每次向前移动一个节点,如果有环则 //fast和slow一定会在环中相遇 int detectAndRemoveLoop(Node node) { Node slow = node, fast = node; while (slow != null && fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; // If slow and fast meet at same point then loop is present if (slow == fast) { removeLoop(slow, node); return 1; } } return 0; } //删除环 void removeLoop(Node loop, Node curr) { Node ptr1 = null, ptr2 = null; /* ptr1从头开始检测是否是环的开始节点 */ ptr1 = curr; while (1 == 1) { /* ptr2围绕着环转一圈如果碰到ptr1则可知ptr1是环的开始节点 */ ptr2 = loop; while (ptr2.next != loop && ptr2.next != ptr1) { ptr2 = ptr2.next; } if (ptr2.next == ptr1) { break; } /* 如果没碰到ptr1,ptr1向后移动一位*/ ptr1 = ptr1.next; } /*ptr2.next与ptr1相遇时可知ptr2是环的最后一个节点,使ptr2指向null来破环 */ ptr2.next = null; } void printList(Node node) { while (node != null) { System.out.print(node.data + " "); node = node.next; } } // Driver program to test above functions public static void main(String[] args) { LinkedList list = new LinkedList(); list.head = new Node(50); list.head.next = new Node(20); list.head.next.next = new Node(15); list.head.next.next.next = new Node(4); list.head.next.next.next.next = new Node(10); // Creating a loop for testing head.next.next.next.next.next = head.next.next; list.detectAndRemoveLoop(head); System.out.println("Linked List after removing loop : "); list.printList(head); }}
解法2(优化解法1)
此方法也依赖于Floyd的周期检测算法。
1)使用Floyd的循环检测算法检测循环并获取指向循环节点的指针。
2)计算循环中的节点数。让计数为k。
3)将一个指针固定到头部,将另一个指针固定到头部的第k个节点。
4)以相同的速度移动两个指针,它们将在循环开始节点处相遇。
5)获取指向循环的最后一个节点的指针,并将其下一个作为NULL。
代码
class LinkedList { static Node head; static class Node { int data; Node next; Node(int d) { data = d; next = null; } } //检测链表中是否有环 int detectAndRemoveLoop(Node node) { Node slow = node, fast = node; while (slow != null && fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; // 如果fast和slow相遇则可知有环调用removeLoop删除环 if (slow == fast) { removeLoop(slow, node); return 1; } } return 0; } // 删除环 void removeLoop(Node loop, Node head) { Node ptr1 = loop; Node ptr2 = loop; //计算环中的节点个数 int k = 1, i; while (ptr1.next != ptr2) { ptr1 = ptr1.next; k++; } // 使ptr1指向头结点, ptr1 = head; // ptr2指向第k个节点。 ptr2 = head; for (i = 0; i < k; i++) { ptr2 = ptr2.next; } /* 同时移动ptr1,ptr2可知当ptr1与ptr2相遇时正好是环的最后一个节点与环的开始节点相遇 */ while (ptr2 != ptr1) { ptr1 = ptr1.next; ptr2 = ptr2.next; } ptr2 = ptr2.next; while (ptr2.next != ptr1) { ptr2 = ptr2.next; } /* 使环的最后一个节点指向null*/ ptr2.next = null; }}
解法3(优化解法2)
我们不需要计算循环中的节点数。在检测到循环之后,如果我们从头开始slow并且以相同的速度移动slow和fast。那么slow和fast最终会在环的开始出相遇。
下面是数学推导:
fast指针移动的距离是慢指针的两倍:
(m+n*x+k)=2*(m+n*y+k)
由上可得到:
m+k=(x-2y)*n;
也就是说m+k是n的倍数。
因此,如果我们开始以相同的速度移动两个指针,使得一个指针(慢指针)从链表的头节点开始并且其他指针(快指针)从会合点开始。当慢指针到达链表的开头(已经做了m步)。快速指针也会移动m步,因为他们现在移动相同的速度。由于m + k是n的倍数,并且从k开始快速,它们将在开始时满足。
class LinkedList { static Node head; static class Node { int data; Node next; Node(int d) { data = d; next = null; } } void detectAndRemoveLoop(Node node) { Node slow = node; Node fast = node.next; // 使用slow和fast来找环 while (fast != null && fast.next != null) { if (slow == fast) { break; } slow = slow.next; fast = fast.next.next; } /* 如果环存在 */ if (slow == fast) { slow = node; while (slow != fast.next) { slow = slow.next; fast = fast.next; } fast.next = null; /* 删除环*/ } } }
- 链表(篇6)Floyd破圈法以及其改进方法检查删除链表中的环
- 项目管理中的问题以及改进方法
- for循环删除(ArrayList.remove)及改进方法及对象在内存中的理解
- for循环删除(ArrayList.remove)及改进方法及对象在内存中的理解
- dbscan算法以及其基于grid建立索引的改进方法
- dbscan算法以及其基于grid建立索引的改进方法
- 求公共子串问题以及其改进算法
- 求公共子串问题以及其改进算法
- 排序:插入排序以及其改进:二分法插入
- c#扩展方法奇思妙用高级篇二:Aggregate扩展其改进
- AOP以及其在Spring中的应用(二)
- AOP以及其在Spring中的应用(二)
- nodejs中的事件机制以及事件协作和雪崩问题的改进方法
- 删除数据库表中的重复冗余记录,以及随机取N条数据的方法
- android删除文件夹以及文件夹中的文件的方法(清晰易懂)
- android中删除文件夹以及文件夹中的文件的方法
- HttpURLConnection 以及其父类URLConnection 中的方法及静态常量
- QTP中的WebTable中的GetRowWithCellText方法改进
- 加快apk的构建速度,如何把编译时间从130秒降到17秒
- 并查集
- 《ACM 书中题目》L
- 并查集初步
- php md5() & sha1() vulnerability
- 链表(篇6)Floyd破圈法以及其改进方法检查删除链表中的环
- L2-008. 最长对称子串
- 禁用 OS X 下的仪表盘教程 转自 少数派
- 《ACM程序设计》 Y题
- 欢迎使用CSDN-markdown编辑器
- lvs_结合keepalived配置
- 使用Java的POI进行Word文档的解析并生成XML格式文档
- Android进阶知识点(再话Activity)
- Android sensors概述