专栏首页健程之道力扣142——环形链表 II

力扣142——环形链表 II

原题

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。

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

示例 1:

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

示例 2:

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

示例 3:

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

进阶:

你是否可以不用额外空间解决此题?

原题url:https://leetcode-cn.com/problems/linked-list-cycle-ii/

解题

在这里贴一下题目所提供的节点结构,这样下面的代码就不重复贴了:

Definition for singly-linked list.
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
        val = x;
        next = null;
    }
}

利用集合

拿到题目的时候,一开始想到的就是利用集合,存储已经遍历过的节点,如果访问到 null,说明不是环;如果添加失败,说明已经添加过,那么一定是环,并且该节点就是环的入口;

顺便说一句,我认为集合所占空间应该不是很大,因为它只是存储对象的应用地址,当然了,集合本身也是一个新的对象,也会占用额外的空间。

让我们看看代码:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null) {
            return null;
        }

        ListNode current = head;
        Set<ListNode> set = new HashSet<>();
        while (current != null) {
            // 添加成功,则继续访问下一个节点
            if (set.add(current)) {
                current = current.next;
                continue;
            }

            // 添加不成功,说明重复
            return current;
        }

        return null;
    }
}

提交OK,执行用时:5 ms,内存消耗:37.7 MB,但是提交用时只战胜了30.99%的 java 提交记录,看来有必要优化一下。

找规律

以前我们判断链表是否有环,都是通过快慢指针最终是否相等。现在的话,因为环可能并不是首尾相连,所以只找一次可能不够了,需要继续寻找规律。

我们假设一开始 slow 指针走过的路程为 x,那么 fast 指针走过的路程就为 2x,即:

s = x;
f = 2x;

如果 fast 指针最终为 null,那么说明不是环。

如果 fast、slow 指针最终指向的节点相等,说明有环,并且, fast 指针比 flow 指针多走了 n 圈环的长度,那么我们假设环的长度为 b,那么可以得出:

f = x + nb;

可以得出:s = nb;

以上就是最重要的结论了,slow 指针其实也已经走了 n 圈环的长度了。那么,我们再假设从 head 节点到环入口节点的长度为 a,那么从快慢指针相遇节点再走 a 步,最终会走到哪儿呢?

最终也会走到环的入口节点,因为(nb + a)可以理解为(a + nb),相当于从 head 节点出发,达到环的入口节点处,又绕环走了 n 圈,所以也会走到环的入口。所以此时我们也找到环的入口节点了。

接下来让我们看看代码:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        // 先利用快慢指针,如果最终能相遇,说明有环
        ListNode slow = head;
        ListNode fast = head;
        while (true) {
            // 快指针为null,说明没有环
            if (fast == null || fast.next == null) {
                return null;
            }

            // 慢指针移动一步
            slow = slow.next;
            // 快指针移动两步
            fast = fast.next.next;
            // 快慢指针相等,说明相遇
            if (fast == slow) {
                break;
            }
        }

        // 再用两个指针,一个从头结点出发,一个从相遇点出发,两个指针每次移动1步,两个指针相遇的地方为环的入口
        slow = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }

        return slow;
    }
}

提交OK,执行用时:1 ms,内存消耗:37.8 MB,但是提交用时只战胜了55.14%的 java 提交记录,难道还有更加高效的方法?

我找了一个执行用时 0 ms 的代码,发现就是和我这个类似的,我将它的代码再次提交后,发现和我这个提交结果一样。看来那些比我们快的算法,可能是因为提交时间比较早,测试案例并不像现在那么多,所以不必担心了。

总结

以上就是这道题目我的解答过程了,不知道大家是否理解了。这道题目主要利用快慢指针,再总结规律,最终也能解决,总的来说是一道很考验逻辑思维的题目。

有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。

https://www.death00.top/

公众号:健程之道

本文分享自微信公众号 - 健程之道(JianJianCoder),作者:健健壮

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 力扣287——寻找重复数

    给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个...

    健程之道
  • 力扣148——排序链表

    原题url:https://leetcode-cn.com/problems/sort-list/

    健程之道
  • Java面试-List中的sort详细解读

    最近看了一些排序相关的文章,因此比较好奇,Java中的排序是如何做的。本篇文章介绍的是JDK1.8,List中的sort方法。

    健程之道
  • JS基础测试: 下列等式返回值是true的是?

    规范中提到, 要比较相等性之前,不能将 null 和 undefined 转换成其他任何值,并且规定null 和 undefined 是相等的。

    舒克
  • TypeScript 非空断言

    使用这种方案,问题是解决了。但有没有更简单的方式呢?答案是有的,就是使用 TypeScript 2.0 提供的非空断言操作符:

    阿宝哥
  • 【LeetCode第178场周赛】5345. 通过投票对团队排名

    如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。

    韩旭051
  • atop使用介绍

    atop用来监控系统资源与进程的工具,默认是以10s为间隔,来记录系统的运行状态,并且会以每隔10分钟记录一次采集数据到日志中。

    dogfei
  • ViewPager实现广告自动轮播核心代码(Handler+Thread)

    用户1737026
  • [linux][system]atop的介绍和使用

    前言 Linux上运行大量的后端的业务程序,往往希望得到更快的响应速度,更小的延迟,甚至有严格的PCT 99的指标。而操作系统的复杂度很高,多个因子之间可能会互...

    皮振伟
  • 函数 | Python的内置函数详解(文末有惊喜)

    Python内置的函数及其用法。为了方便记忆,已经有很多开发者将这些内置函数进行了如下分类:

    潘永斌

扫码关注云+社区

领取腾讯云代金券