23: 链表中环的入口节点

如果一个链表中包含环,找出环的入口节点
首先确定链表中有环:快指针一次走两步,慢指针一次走一步。如果链表中有环,两指针必定相交。

为什么可以相交?假设链表结构为 1->2->3->4->5->6->7->8->3,慢指针指向6

  1. 假设快指针指向 5, 此时下一步一定会相交
  2. 假设快指针指向4, 此时下一步会跳到上面的状态
  3. 快指针一直在环内移动,并且必然会存在慢指针在快指针之前这种情况,之后移动的过程中,两指针一定会进入上面两种状态。(因为快指针移动步长是2)

那么如何找到入口节点呢?

首先想到的思路是用 set 集合,遍历链表,首先判断set集合中是否包含该节点,如果不包含则添加,包含则该节点就是入口节点。这种方法下时间复杂度O(n), 空间复杂度O(n)

public static Node hasLoop(Node head) {
    if (head == null)
        return null;
    Node slow = head;
    Node fast = head;
    while (fast.next != null && fast.next.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) {
            return slow;
        }
    }

    return null;
}

public static Node entryNodeOfLoop(Node head) {
    if (hasLoop(head) == null) {
        return null;
    }

    Node node = head;
    Set<Node> set = new HashSet<>();
    while (head != null) {
        if (set.contains(node)) {
            return node;
        } else {
            set.add(node);
            node = node.next;
        }
    }

    return null;
}

书上的思路是,找到相交点 meetNode, 通过交点可以求出环的长度 n,思想是,一个指针从入口开始走 n 步又回到入口节点。

指针B先走n步,之后指针A,B同时前进。当指针 A 到达入口节点时,指针B恰好绕一圈又回到入口节点。

public static Node meetNode(Node head) {
    Node meetNode = hasLoop(head);
    if (meetNode == null) {
        return null;
    }

    int length = 1;
    Node help = meetNode;
    while (help.next != meetNode) {
        help = help.next;
        length++;
    }
    Node A = head;
    Node B = head;
    for (int i = 1; i<= length; i++) {
        A = A.next;
    }
    while (A != B) {
        A = A.next;
        B = B.next;
    }

    return A;
}

猜你喜欢

转载自blog.csdn.net/weixin_41889284/article/details/89418122