链表中环的入口节点

链表中环的入口节点

1、参考资料

https://www.nowcoder.com/practice/6e630519bf86480296d0f1c868d425ad

https://blog.nowcoder.net/n/c42a259697014745b1688f9c6795d206

2、题目要求

对于一个给定的链表,返回环的入口节点,如果没有环,返回 null

拓展:你能给出不利用额外空间的解法么?

3、代码思路

方法一:使用 HashSet 记录链表节点地址

怎么才算是环的入口?遍历链表,重复出现的第一个节点便是环的入口

遍历链表中每个节点,每遍历一个节点时,看下 Set 集合里面有没有该节点,如果有,则证明这是环的入口,将该节点返回;如果没有,则将其添加至 Set 集合中

如果遍历完整个链表,都没有找到重复的节点,则证明该链表没有环,return null;


方法二:快慢指针法

题目【判断链表是否有环】中使用到了快慢指针法,但只能保证 slowfast 指针在环中的某一个节点相遇,那么如何通过快慢指针找到入口节点呢?

假设 fast 指针和 slow 指针在 Z 点相遇,此时 fast 指针走过的距离为 a+b+n*(b+c)(其实 n 为快指针在环中走的圈数),slow 指针走过的距离为 a+b,因为 fast 指针速度为 slow 指针速度的两倍,所以 a+b+n*(b+c) = 2*(a+b),可得出 a=(n-1)*(b+c)+c,其中 (b+c) 为链表环形部分的长度

上式说明什么?如果 fastslow 相遇时,我们让 slow 回到起点 X 处,再让 slowfast 以相同的速度(每次走一步)往后移动,当 slow 向后移动 a 步时,fast 走了 (n-1) 圈外加向后移了 c 步,所以 slowfast 会相遇在 Y 点(环形入口点)

image-20200908161323213

4、代码实现

方法一:使用 HashSet 记录链表节点地址

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

    class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }

方法二:快慢指针法

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head; // 慢指针
        ListNode fast = head; // 快指针

        // 快指针每次需要向后移动两步,所以需要判断 fast.next 是否为 null
        while (fast != null && fast.next != null) {
            slow = slow.next; // 慢指针走一步
            fast = fast.next.next; // 快指针走两步
            // 当快慢指针相等时,证明链表有环
            if (slow == fast) {
                slow = head; // 让 slow 重新回到首节点
                // 两个指针以相同的速度向后走,相遇在入口节点
                while (slow != fast) {
                    slow = slow.next;
                    fast = fast.next;
                }
                return slow;
            }
        }

        return null;
    }
}

class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
        next = null;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值