• 【牛客-剑指offer-数据结构篇】JZ23 链表中环的入口结点 两种实现 Java实现



    1 题目链接

    https://www.nowcoder.com/exam/oj/ta?page=1&tpId=13&type=13

    2 题目

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

    3 思路 & 代码

    方案一 HashSet

    遍历链表,把遇到的节点全都放入hashset中,每次在向set中添加元素之前,先检查一下set中是否已经包含此节点:

    • 如果包含,则表示该节点就是链表的环的入口,直接返回该节点即可

    • 如果链表遍历结束,还没有出现重复的节点,则说明该链表中没有环,返回null

    import java.util.*;
    /*
     public class ListNode {
        int val;
        ListNode next = null;
    
        ListNode(int val) {
            this.val = val;
        }
    }
    */
    public class Solution {
    
        public ListNode EntryNodeOfLoop(ListNode pHead) {
    
            if (pHead == null) {
                return null;
            }
    
            HashSet<ListNode> set = new HashSet<>();
            ListNode tmp = pHead;
            while (tmp != null) {
                if (set.contains(tmp)) {
                    return tmp;
                }
                set.add(tmp);
                tmp = tmp.next;
            }
            return null;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    方案二 快慢指针

    指定两个指针slow和fast,fast每次后移两个,slow每次后移一个。fast指针的速度是slow指针的两倍,相遇时所经历的时间是一样的,所以fast指针所走的距离时slow指针所走距离的了两倍。

    假设:

    • 从头节点到链表的环的入口的距离为x
    • 从环的入口到相遇地点的距离是y
    • 从相遇地点再走到环的入口的距离是z
    • 在相遇之前,fast指针走了n个完整的环,slow指针走了m个完整的环,0<=m

    由前面得出的fast和slow所走的距离的关系,可得出:

    x + n(y + z) + y ------ fast指针走的距离

    x + m(y + z) + y ------ slow指针走的距离

    x + n(y + z) + y = 2[x + m(y + z) + y]

    两边移项

    x + y = (n - 2m)(y + z)

    其中,

    y + z刚好是环的大小;x + y是从头节点,经过环的入口到达相遇节点的距离;

    也就是说,从头节点,经过环的入口到达相遇节点的距离环的大小的整数倍

    在fast和slow相遇以后,将其中一个指针移到头节点,然后两个指针以每次移动一个的速度前进

    最终,两个指针会再次同时到达相遇地点,当两个节点再次到达相遇地点之前,肯定已经先进入环了,在环中共同走过一段,所以,两个指针第一次相等的节点,就是环的入口。

    import java.util.*;
    /*
     public class ListNode {
        int val;
        ListNode next = null;
    
        ListNode(int val) {
            this.val = val;
        }
    }
    */
    public class Solution {
    
        public ListNode EntryNodeOfLoop(ListNode pHead) {
    
            //空链表
            if (pHead == null) {
                return null;
            }
    
            ListNode fast = pHead;
            ListNode slow = pHead;
    
            while (fast != null && fast.next != null) {
                //fast移动两次
                fast = fast.next.next;
                //slow移动一次
                slow = slow.next;
                
                //fast和slow相遇,说明链表中有环
                if (fast == slow) {
                    //将fast指针移向头节点
                    fast = pHead;
                    while (fast != slow) {
                        //fast和slow移动一次
                        //再次相遇的地点就是环的入口
                        fast = fast.next;
                        slow = slow.next;
                    }
                    return slow;
                }
            }
            //如果while循环结束,说明fast指针先到达链表的结尾,链表中没有环,返回null
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
  • 相关阅读:
    React中ref的使用方法和使用场景(详解)
    【LeetCode-112】路径总和
    想要精通算法和SQL的成长之路 - 二叉树的判断问题(子树判断 | 对称性 | 一致性判断)
    NOIP 2013 普及组初赛试题
    Docker容器化技术
    一文读懂Elephant Swap,为何为ePLATO带来如此高的溢价?
    L2研发工程师—牛客网
    设计模式原则——单一职责原则(SPS)
    docker 安装 nginx
    悄然兴起的“跑腿”,正在成为一个千亿市场
  • 原文地址:https://blog.csdn.net/guliguliguliguli/article/details/126195358