干tm的算法8:环形链表 II

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

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

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

进阶:

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

示例 1:

在这里插入图片描述

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

在这里插入图片描述

输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。 示例
3:

在这里插入图片描述

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

提示:

链表中节点的数目范围在范围 [0, 104] 内
-105 <= Node.val <= 105 pos 的值为 -1 或者链表中的一个有效索引

来源:力扣(LeetCode)
链接: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;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
         Set<ListNode> set=new HashSet<>();
        ListNode ls=head;
        while (ls!=null){
            if (!set.add(ls)){
                return ls;
            }
            ls=ls.next;
        }
        return null;
    }
}
执行用时:
4 ms
, 在所有 Java 提交中击败了
28.06%
的用户
内存消耗:
39.3 MB
, 在所有 Java 提交中击败了
23.92%
的用户

我的解题。思路前一位形成闭环的话,会回带前置位的下标处。二者比较大小,如果.next的下标小于当前的下标,那么就说明形成闭环了。题目我个人还是觉得首先要从自己可以理解的答案入手,你能理解说明这也比较符合你的水平。然后进行优化。
代码解读

/**
 * Definition for singly-linked list.
 * /** 链表的定义  有一个val 当前值  以及 下一个链表 链表有无序性*/
 * class ListNode {
 *     int val;
 *     ListNode next;
 *      /** 对应赋值  */
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        /** 创建一个set 不重复的数据 */
         Set<ListNode> set=new HashSet<>();
         /** 基本赋值 */
        ListNode ls=head;
        /** 循环直到有返回值为止 */
        while (ls!=null){
            /** 如果出现重复的节点,就说明重复了 */
            if (!set.add(ls)){
                /** 如果放不进去就直接返回 */
                return ls;
            }
            ls=ls.next;
        }
        return null;
    }
}

解读完之后,我发现这种解法存在很大漏洞。链表节点是无序的,如果某几节点都一样,也就是当前节点值跟下一个节点值都一样。那么这个是否算同一个,那么返回是返回哪个。是否就出现错误。
看一下官方解答

方法一:哈希表 思路与算法

一个非常直观的思路是:我们遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。借助哈希表可以很方便地实现。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode pos = head;
        Set<ListNode> visited = new HashSet<ListNode>();
        while (pos != null) {
            if (visited.contains(pos)) {
                return pos;
            } else {
                visited.add(pos);
            }
            pos = pos.next;
        }
        return null;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在这里插入图片描述
在这里插入图片描述

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode slow = head, fast = head;
        while (fast != null) {
            slow = slow.next;
            if (fast.next != null) {
                fast = fast.next.next;
            } else {
                return null;
            }
            if (fast == slow) {
                ListNode ptr = head;
                while (ptr != slow) {
                    ptr = ptr.next;
                    slow = slow.next;
                }
                return ptr;
            }
        }
        return null;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在这里插入图片描述

有二种解法。解法1我能比较快理解。对应是通过不可重复性,来进行判断,如果重复存在,那么就是回环了。
解法2解析:

/**
 * Definition for singly-linked list.
 *  链表的定义  有一个val 当前值  以及 下一个链表 链表有无序性
 * class ListNode {
 *     int val;
 *     ListNode next;
 *       对应赋值  
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        /** 基本判空处理起手式 如果只有一个其实也一样 */
        if (head == null) {
            return null;
        }
        /** 再复制一个链表 */
        ListNode slow = head, fast = head;
        /** 下面是一个快 一个慢  错开进行处理 */
        /** 快的没有到最后一个 */
        while (fast != null) {
            /** 慢的变成慢的下一个 慢走一步*/
            slow = slow.next;
            /** 如果快的下一个不是null */
            if (fast.next != null) {
                /** 那么快就是下一个的下个 快走二步*/
                fast = fast.next.next;
            } else {
                /** 如果是null 那么就是对比完成,可以都没有闭环 返回对应的null  如果是闭环了就不会出现null,快的始终会有一个时间追上慢的*/
                return null;
            }
            /** 快追上慢的 */
            if (fast == slow) {
                /** 证明闭环是存在的 */
                ListNode ptr = head;
                /** 闭环这个点跟slow存在某个点是吻合 */
                while (ptr != slow) {
                    /** 不相等就继续移动,总会一个相等 */
                    ptr = ptr.next;
                    slow = slow.next;
                }
                /** 返回这个相等节点 */
                return ptr;
            }
        }
        return null;
    }
}

在这里插入图片描述
这里是解法精髓。我们再好好品品。
这里 a 代表链表环外部分长度。距离b是对应快慢相遇的距离 距离c是这个闭环的剩余走完一圈距离
相遇时候。快的走了 a+n(b+c)+b = a+ (n+1)b + nc 距离
快的经过是慢的2倍。
从慢的角度来说 他进入闭环,因为快的步长是慢的2倍。所以不存在慢的能在闭环中走完任何一个闭环长度还没有被快的追上。除非是刚到闭环,a就走完一圈了。那么b就是=0.不然慢的经过路径都是a+b
所以可以等到 a+(n+1)b+nc = 2a + 2b 换算一下 可以得到 a = c + (n-1)(b+c)
这里b+c 是闭环长度 c 是慢的剩余走完闭环的步长、所以如果这时候某另起一个,跟慢的同时进行一步一步行走。那么当另一个走完a,对应慢的也走完了c。
是否可以认为 a = c 。这样就忽略了闭环长度是多少。只需要进行某个他们碰撞时候,对饮另一个走到哪里了,就是a长度了。这就很妙.


漫漫长路,一个小周跟他一个小陈朋友一起努力奔跑。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值