Loading

环形链表 II

1.问题描述

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 10<sup>4</sup>]
  • -10<sup>5</sup> <= Node.val <= 10<sup>5</sup>
  • pos-1 或者链表中的一个 有效索引

2.求解

哈希表法

本题与环形链表的不同点在于需要返回入环的第一个节点,遍历整个链表,把每个节点存入哈希表中,如果遇到存储失败,则说明已经遍历到第一个环节点的位置了。

代码如下
/*
* 执行用时:7 ms, 在所有 Java 提交中击败了6.84% 的用户
* 内存消耗:39.7 MB, 在所有 Java 提交中击败了18.85% 的用户
* */
public ListNode detectCycle(ListNode head) {
    Set<ListNode> lSet = new HashSet<ListNode>();
    while (head != null) {
        boolean add = lSet.add(head);
        if (!add){
            break;
        }
        head = head.next;
    }
    return head;
}
  • 时间、空间复杂度均为O(n),需要遍历长度为n的链表,使用了n的空间存储每个链表

快慢指针

由于题目要求返回入环的第一个节点,所以我们需要用一点数学技巧求得第一个节点。

  • 假设链表直线部分有a个节点,环形部分有b个节点,设置快慢指针slow、fast从头节点出发,慢指针每次走一步,快指针每次走两步
  • 若两指针相遇,则说明链表有环,当第一次相遇时,由于快指针速度是慢指针的两倍,所以走过的距离一定是慢指针的两倍,得到第一个数学公式f = 2s,设快指针此时已经比慢指针多绕环n圈,则得到第二个数学公式f = s + nb
  • 两公式相减,得s = nb、f = 2nb,所以慢指针此时绕环n圈,快指针此时绕环2n圈
  • 那么我们如何寻找环的入口节点呢?如果我们令一个指针走到环入口节点的步数为k,那么这个k应该满足什么条件呢,k = a + nb,走a步到入口节点,然后随便绕环几圈既可以了,那么现在明白了,慢指针距离入口节点还需要a步
  • 但是不知道a的值,如何定位这个a步呢,此时我们令快指针从头部开始以步长为1开始走,当走a步时,就会到达环入口节点,于是当两指针相遇时,此时一定走了a步,一定是在环的入口节点
代码如下
/*
执行用时:1 ms, 在所有 Java 提交中击败了38.00% 的用户
内存消耗:38.9 MB, 在所有 Java 提交中击败了65.93% 的用户
* */
public ListNode detectCycle(ListNode head) {
    ListNode slow = head;
    ListNode fast = head;
    if(head == null|| head.next == null){
        return null;
    }
    while(true){
        slow = slow.next;
        fast = fast.next.next;
        if(fast == null || fast.next == null){
            return null;
        }
        if(slow == fast){
            break;
        }
    }
    fast = head;
    while(slow != fast){
        slow = slow.next;
        fast = fast.next;
    }
    return fast;
}
  • 时间复杂度:O(n),n即为节点数目,时间复杂度只讨论n的量级.O(kn)也是O(n)的复杂度,只要这个k小于n,而在本题中不会出现套n圈的情况
  • 空间复杂度:O(1),只使用了常数级的存储空间
posted @ 2020-10-10 16:34  水纸杯  阅读(106)  评论(0)    收藏  举报