LeetCode---142. 环形链表 II

题目:142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

进阶:你是否可以使用 O(1) 空间解决此题?


思路

简单的解法就是使用HashSet,利用元素不可重复的特性找到入环节点,我们这里不做讲解,底下附有实现代码
另一种做法就是利用快慢指针找环入口
我们详细说明一下快慢指针的解法
设置两个指针fastslow,其中fast一次跨两步,slow一次一步,现在有两种情况:

  1. 链表无环,fastslow永远不会相遇,当fast为空或者fast.next为空时,返回null结束
    LeetCode---142. 环形链表 II
  2. 链表有环(如上图),那么fastslow相遇必定是在环内,现在假设,环外的长度为afastslow相遇的点为b,从点b到环入点的距离为c
    • 因为fast一次走两步,所以,当两个指针相遇时,fast走过的距离为 a + n(b + c) + b,(未知数nfast走过的圈数)
    • slow一次走一步,此时slow走过的距离为 a + b ,注意,这里存在迷惑点,可能有人会觉得为什么slow会在走不完一圈的情况下与fast相遇(参考自 @astrophel 这位老哥的评论,很清晰)
      • 这里我们可以这样想,第一步,快指针fast先进入环
      • 第二步,慢指针slow进入环,此时fastslow相距 x,x一定是大于等于0的,假设环的长度为n,那么fast需要追赶slow的长度就为n-x
      • 这里可以看成fast的速度是2步每秒,slow的速度是1步每秒,相当于fast每秒钟离slow的距离就会拉近一步,那么fast要追赶到slow就需要n-x秒,在这n-x秒中,slow走了n-x步,而 x >= 0,slow走的路程就小于等于n
      • 即慢指针走不完一圈,就会与快指针相遇
    • 接着,因为fast一次走两步,而slow一次走一步,可以得知,fast走过的路程是slow 的两倍,也就是 a + n(b + c) + b = 2(a + b),化简可以得出 a = (n - 1)(b + c) + c
    • a 也就是从头指针到环入口的距离,等于(n - 1)圈环的距离再加上c,而c就是快慢指针相遇的点距环入口的距离
    • 所以我们可以将fast重新设置指向head,slow位置不变,slowfast同时每轮向前走一步
      • fast走过的路程为 a 的同时,slow 走过的路程为 c,此时两者再次相遇,并且同时指向环的入口
    • 返回fastslow结束

代码

//1. 使用HashSet解决问题
public class Solution {
    public ListNode detectCycle(ListNode head) {
        Set<ListNode> set = new HashSet<>();
        while (head != null) {
            if (!set.add(head)) {
                return head;
            }
            head = head.next;
        }
        return null;
    }
}
//2. 使用快慢指针解决问题
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (true) {
            if (fast == null || fast.next == null)
                return null;
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow)
                break;
        }
        fast = head;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }
}
上一篇:【142期】阿里面试:分析为什么B+树更适合作为索引的结构以及索引原理


下一篇:力扣141题、142题(快慢指针)