java算法判断链表有没有闭环_【算法问题】如何判断链表有环

如何判断链表有环

摘自漫画算法:

题目:有一个单向链表,链表中有可能出现“环”,就像下图这样,那么如何用程序来判断该链表是否为有环链表呢?

9434033bbead9a16eab79d0b08b4f2c8.png

方法1

首先从头节点开始,以此遍历单链表中的每一个节点。每遍历一个新节点,就从头检查新节点之前的所有节点,用新节点和此节点之前所有节点依次做比较。如果发现新节点和之前的某个节点相同,则说明该节点被遍历过两次,链表有环。如果之前的所有节点中不存在与新节点相同的节点,就继续遍历下一个新节点,继续重复刚才的操作。

5ef3eb18f3f682a4e1e10382c0db06dc.png

就像图中这样,当遍历链表节点7时,从头访问节点5和3,发现已遍历的节点中并不存在节点7,则继续往下遍历。

当第2次遍历到节点2时,从头访问曾经遍历过的节点,发现已经遍历过节点2,说明链表有环。

假设链表的节点数量为n,则该解法的时间复杂度为O(n²)。由于并没有创建额外的存储空间,所以空间复杂度为O(1)。

方法2

首先创建一个以节点ID为key的HashSet集合,用来存储曾经遍历过的节点。然后同样从头节点开始,依次遍历单链表中的每一个节点。每遍历一个新节点,都用新节点和HashSet集合中存储的节点进行比较,如果发现HashSet中存在与之相同的节点ID,则说明链表有环,如果HashSet中不存在与新节点相同的节点ID,就把这个新节点ID存入HashSet中,之后进入下一节点,继续重复刚才的操作。

b33719d2d0d563eb00115b3981030f58.png

遍历过5、3、7、2、6、8、1。

a1e946893e2cca4088c41b804f81554c.png

当再次遍历节点2时,查找HashSet,发现节点已存在。

7c503c50f660cf4160f22da380e324e2.png

由此可知,链表有环。

这个方法在流程上和方法1类似,本质的区别是使用了HashSet作为额外的缓存。假设链表的节点数量为n,则该解法的时间复杂度是O(n)。由于使用了额外的存储空间,所以算法的空间复杂度同样是O(n)。

方法3

首先创建两个指针p1和p2(在Java里就是两个对象引用),让它们同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针p1每次向后移动1个节点,让指针p2每次向后移动2个节点,然后比较两个指针指向的节点是否相同。如果相同,则可以判断出链表有环,如果不同,则继续下一次循环。

步骤如下:

1、p1和p2都指向节点5

d4e799b51f6ec87136abeb59d0bf50eb.png

2、p1指向节点3,p2指向节点7

30f3a254fb3fd5e4634f08da2a7b0358.png

3、p1指向节点7,p2指向节点6

dade999accacbf5a72ce013d699d01a0.png

4、p1指向节点2,p2指向节点1

2994967722b2f47d7a1fd45761b46ad4.png

5、p1指向节点6,p2也指向节点6,p1和p2所指相同,说明链表有环

a4b2a36f69d1d3ef96d60ae746bba726.png

学过小学奥数的读者,一定听说过数学上的追及问题。此方法就类似于一个追及问题。

在一个环形跑道上,两个运动员从同一起点炮,一个运动员速度快,另一个运动员速度慢。当两人跑了一段时间后,速度快的运动员必然会再次追上并超过速度慢的运动员,原因很简单,因为跑道是环形的。

假设链表的节点数量为n,则该算法的时间复杂度为O(n)。除两个指针外,没有使用任何额外的存储空间,所以空间复杂度是O(1)。

代码实现:

/**

* Create By ZhangBiao

* 2020/6/4

*/

public class LinkedList {

/**

* 链表节点

*/

private static class Node {

int data;

Node next;

public Node(int data) {

this.data = data;

}

}

/**

* 判断链表是否有环

*

* @param head 链表头节点

* @return

*/

public static boolean isCycle(Node head) {

Node p1 = head;

Node p2 = head;

while (p2 != null && p2.next != null) {

p1 = p1.next;

p2 = p2.next.next;

if (p1 == p2) {

return true;

}

}

return false;

}

public static void main(String[] args) {

Node node1 = new Node(5);

Node node2 = new Node(3);

Node node3 = new Node(7);

Node node4 = new Node(2);

Node node5 = new Node(6);

node1.next = node2;

node2.next = node3;

node3.next = node4;

node4.next = node5;

node5.next = node2;

System.out.println(isCycle(node1));

}

}

问题扩展

1、如果链表有环,如何求出环的长度?

e2aa62604cf1df8051daf7f6cf1fa73d.png

解法如下:

当两个指针首次相遇,证明链表有环的时候,让两个指针从相遇点继续循环前进,并统计前进的循环次数,知道两个指针第2次相遇。此时,统计出来的前进次数就是环长。

因此指针p1每次走1步,指针p2每次走2步,两者的速度差是1步。当两个指针再次相遇时,p2比p1多走了整整1圈。因此,环长 = 每一次速度差 * 前进次数 = 前进次数。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值