剑指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;
        }
        //2
        System.out.println(result.val);
    }

    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;
        //尾节点和节点2构成环
        head4.next = head2;
        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 the p head
     * @return list node
     */
    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 the node
     * @return int
     */
    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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值