剑指Offer-题23(Java版):链表中环的入口结点

参考自:《剑指Offer——名企面试官精讲典型编程题》

题目:链表中环的入口结点
若一个链表中包含环,如何找出环的入口结点?

主要思路
第一步判断链表是否有环。使用双指针,一个指针(慢指针)一次走一步,另一个指针(快指针)一次走两步。如果快指针追得上慢指针,说明有环,否则链表无环。
第二步求环的长度L。第一步中的快慢指针相遇的结点一定在环中(因为只有快慢指针在环中绕圈时,才能相遇)。因此,从这个相遇的结点出发,一边移动,一边计数,当再次回到该结点时,便可得出环的长度L。
第三步找到环的入口。使用双指针,第一个指针先走L步,第二个指针再从头开始移动,两个指针速度都是每次一步。当第二个指针指向环的入口时,第一个指针已经围绕环走了一圈回到入口处,即两个指针相遇的结点即为环的入口。

关键点:化繁为简,分步骤

时间复杂度:O(n)

public class EntryNodeOfLoop
{
    public static void main(String[] args)
    {
        ListNode head = testData();
        ListNode result = entryNodeOfLoop(head);
        if (result == null) return;
        System.out.println(result.val); //2
    }

    private static ListNode testData()
    {
        ListNode head = new ListNode(1);
        ListNode head2 = new ListNode(2);
        ListNode head3 = new ListNode(3);
        ListNode head4 = new ListNode(4);
        head.next = head2;
        head2.next = head3;
        head3.next = head4;
        head4.next = head2;  //尾节点和节点2构成环
        return head;
    }

    private static ListNode entryNodeOfLoop(ListNode pHead)
    {
        //查找相遇的节点
        ListNode meetingNode = findMeetNode(pHead);
        if (meetingNode == null) return null;
        //计算环的长度
        int loopLength = computeLoopLength(meetingNode);

        ListNode before = pHead; //前面的快指针
        ListNode after = pHead; //后面的慢指针
        for (int i = 0; i < loopLength; i++)
        {
            before = before.next;
        }
        while (before.val != after.val)
        {
            before = before.next;
            after = after.next;
            if (before.val == after.val) break;
        }
        return before;
    }

    /**
     * 查找相遇的节点
     *
     * @param pHead
     * @return
     */
    private static ListNode findMeetNode(ListNode pHead)
    {
        if (pHead == null) return null;
        ListNode slow = pHead.next;
        if (slow == null) return null;
        ListNode fast = slow.next;
        while (slow != null && fast != null)
        {
            if (slow.val == fast.val) return fast;
            //慢指针走一步
            slow = slow.next;
            //快指针走两步
            fast = fast.next;
            if (fast != null) fast = fast.next;
        }
        return null;
    }

    /**
     * 计算环的长度
     *
     * @param node
     * @return
     */
    private static int computeLoopLength(ListNode node)
    {
        ListNode runNode = node;
        int length = 1;
        runNode = runNode.next;
        //回到原节点,则求出长度
        while (node.val != runNode.val)
        {
            runNode = runNode.next;
            length++;
        }
        return length;
    }
}

class ListNode
{
    int val;
    ListNode next = null;

    ListNode(int val)
    {
        this.val = val;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_37862405/article/details/80208912