当前位置:   article > 正文

面试题 02.08. 环路检测-快慢指针+如何找到环的入口?(证明)Java_快慢指针找环

快慢指针找环

1.题目

 

2.思路

方法一——哈希表记录节点

思路很简单,记录一下每个节点出现的次数,如果某个节点出现了两次,代表此时有环,并且是环的入口,直接返回即可。

时间复杂度O(N)

空间复杂度O(N)

  1. public class Solution {
  2. public ListNode detectCycle(ListNode head) {
  3. ListNode pos = head;
  4. Set<ListNode> visited = new HashSet<ListNode>();
  5. while (pos != null) {
  6. if (visited.contains(pos)) {
  7. return pos;
  8. } else {
  9. visited.add(pos);
  10. }
  11. pos = pos.next;
  12. }
  13. return null;
  14. }
  15. }

方法二——快慢指针找到环 + temp指针找到环的入口

想法:首先把问题拆分成两个问题,第一个问题判断是否有环。第二个问题,如果有环的话,如何判断环的入口呢?并且保证空间复杂度为O(1)。

第一个问题很好解决。直接快慢指针就可以。

slow指针走一步

fast指针走两步

如果链表存在环,那么必然fast指针和slow指针会相遇。为什么呢?

简单的正面如下:

假如slow刚好走到环的入口,此时fast指针必然在环中的任意一个位置,此时他们之间的距离为length, 这个长度length 必然小于环的大小。

每一次,fast 都会比slow多走一步,所以下次他们之间的距离为length-1.

再下一次他们的距离为length - 2.

...

直到他们两相遇!!!

并且可以得到的是,slow不会走过一圈,他们就会相遇!因为这个距离length小于环的大小。

如果链表存在环,那么如何找到环的入口呢?(变形:如何求出环的大小?)

先说结论。此时定义一个temp指针,指向 head, 让slow 和 temp同时移动,他们相等的时候就是环的入口。

证明如下:(借用官方题解的一个图)

a : 起点到环的入口的举例为a

b:环的入口到快慢指针相遇的位置。

c:环的大小减去b的距离

首先可以得知slow指针移动的距离: a + b

然后可以得知fast指针移动的距离:a + b + n * (b + c)

然后利用fast指针速度是slow指针的两倍,所以在相同的时间下,距离也应该是两倍关系。

所以有2 *(a + b) = a + b + n * (b + c)

因为我们要计算a,所以我们想办法把a放在左边,那么化简可以得到

a = nb - b + nc

这样看不出来什么东西,所以进一步想,因为有环的大小为b + c,所以想办法凑在一起。有

a = (n - 1) * (b + c) + c

这时候可以发现,

左边的值等于一个temp指针从起点走到环的入口

右边的值相当于是slow指针先走距离c, 到达环的入口,然后再走n - 1 圈,直到两个相遇!!!!!!!

所以有结论:当快慢指针相等的时候,让slow 和 temp同时移动,他们相等的时候就是环的入口。

时间复杂度O(N)

空间复杂度O(1)

  1. /**
  2. * Definition for singly-linked list.
  3. * class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) {
  7. * val = x;
  8. * next = null;
  9. * }
  10. * }
  11. */
  12. public class Solution {
  13. public ListNode detectCycle(ListNode head) {
  14. // 时间O(n)
  15. // 空间O(1),这里使用的是三个指针,不占用空间
  16. ListNode slow = new ListNode(-1), fast = new ListNode(-1);
  17. slow = head;
  18. fast = head;
  19. while(fast != null){
  20. if(fast.next != null && fast.next.next != null){
  21. fast = fast.next.next;
  22. }
  23. else
  24. return null;
  25. slow = slow.next;
  26. if(slow == fast){ //代表有循环
  27. // 找到环的入口
  28. ListNode temp = head;
  29. while(temp != slow){
  30. temp = temp.next;
  31. slow = slow.next;
  32. }
  33. return temp;
  34. }
  35. }
  36. return null;
  37. }
  38. }

3.结果

4.类似的题

判断是否有环?

判断环的大小?

删除倒数第K个数?

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/895254
推荐阅读
相关标签
  

闽ICP备14008679号