面试算法22:链表中环的入口节点(2)

题目

如果一个链表中包含环,那么应该如何找出环的入口节点?从链表的头节点开始顺着next指针方向进入环的第1个节点为环的入口节点。

例如,在如图4.3所示的链表中,环的入口节点是节点3。

分析

第1步:确认是否包含环

定义两个指针并同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。如果链表中不包含环,走得快的指针直到抵达链表的尾节点都不会和走得慢的指针相遇。如果链表中包含环,走得快的指针在环里绕了一圈之后将会追上走得慢的指针。因此,可以根据一快一慢两个指针是否能够相遇来判断链表中是否包含环。

第2步:如何找到环的入口节点

上述代码需要求出链表的环中节点的数目。如果仔细分析,就会发现其实并没有必要求得环中节点的数目。如果链表中有环,快慢两个指针一定会在环中的某个节点相遇。慢的指针一次走一步,假设在相遇时慢的指针一共走了k步。由于快的指针一次走两步,因此在相遇时快的指针一共走了2k步。因此,到相遇时快的指针比慢的指针多走了k步。另外,两个指针相遇时快的指针比慢的指针在环中多转了若干圈。也就是说,两个指针相遇时快的指针多走的步数k一定是环中节点的数目的整数倍,此时慢的指针走过的步数k也是环中节点数的整数倍。

此时可以让一个指针指向相遇的节点,该指针的位置是之前慢的指针走了k步到达的位置。接着让另一个指针指向链表的头节点,然后两个指针以相同的速度一起朝着指向下一个节点的指针移动,当后面的指针到达环的入口节点时,前面的指针比它多走了k步,而k是环中节点的数目的整数倍,相当于前面的指针在环中转了k圈后也到达环的入口节点,两个指针正好相遇。也就是说,两个指针相遇的节点正好是环的入口节点。

java 复制代码
public class Test {
    public static void main(String[] args) {
        ListNode listNode1 = new ListNode(1);
        ListNode listNode2 = new ListNode(2);
        ListNode listNode3 = new ListNode(3);
        ListNode listNode4 = new ListNode(4);
        ListNode listNode5 = new ListNode(5);
        ListNode listNode6 = new ListNode(6);

        listNode1.next = listNode2;
        listNode2.next = listNode3;
        listNode3.next = listNode4;
        listNode4.next = listNode5;
        listNode5.next = listNode6;
        listNode6.next = listNode3;
        ListNode result = detectCycle(listNode1);
        System.out.println(result.val);
    }

    public static ListNode detectCycle(ListNode head) {
        ListNode inLoop = getNodeInLoop(head);
        if (inLoop == null) {
            return null;
        }

        ListNode node = head;
        while (node != inLoop) {
            node = node.next;
            inLoop = inLoop.next;
        }

        return node;
    }

    // 快慢指针找到相遇的节点
    private static ListNode getNodeInLoop(ListNode head) {
        if (head == null || head.next == null) {
            return null;
        }

        ListNode slow = head.next;
        ListNode fast = slow.next;
        while (slow != null && fast != null) {
            if (slow == fast)
                return slow;

            slow = slow.next;
            fast = fast.next;
            if (fast != null)
                fast = fast.next;
        }

        return null;
    }
}
相关推荐
大熊背2 分钟前
双目拼接竖缝消除(ISP 分区锐化实操方案) 优化方案
人工智能·算法·双目拼接
_日拱一卒6 分钟前
LeetCode:105从前序与中序遍历序列构造二叉树
算法·leetcode·职场和发展
MicroTech20256 分钟前
微算法科技(NASDAQ :MLGO)发布基于NEQR技术的新型量子视频处理算法,重构智能视觉底层逻辑
科技·算法·音视频
techdashen9 分钟前
Async Rust 近况补课:从 `async-trait` 到原生 async trait
网络·算法·rust
一行代码一行诗++10 分钟前
循环的嵌套
数据结构·算法
玖釉-18 分钟前
C++ 中的矩阵介绍:以二维矩阵查找为例
c++·windows·算法·矩阵
ECT-OS-JiuHuaShan19 分钟前
存在是微分张量积,标量是参数但不可能是本质。还原论泛化,是语义劫持和以偏概全的逻辑谋杀伪科学庞氏骗局
数据库·人工智能·算法·机器学习·数学建模
CQU_JIAKE22 分钟前
5.22【A】
算法
AI人工智能+电脑小能手31 分钟前
【大白话说Java面试题 第68题】【JVM篇】第28题:对于 JDK 自带的监控和性能分析工具用过哪些?一般你怎么用的?
java·开发语言·jvm·面试
2601_9578822434 分钟前
多账号流量内容运营的数据归因与ROI优化:从经验驱动到算法决策的技术转型
算法·产品运营·内容运营