求两个可能有环或无环的链表的交点

给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null
【要求】
如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。

一、分析

要么两个都有环,要么两个都无环
如果一个有环,则代表无止尽,另一个无环,代表有止尽,则代表不相交

二、思路

综上可分为两部分 1. 求两个链表的环点,2. 求其交点
其中2可分为 双方都有环 和双方都无环的情况

三、代码

返回当前链表的环点
使用快慢指针法,慢指针每次走一步,快指针每次走两步
当快指针走到Null,则说明无环点,直接返回
如果快指针和慢指针相交,则说明有环点,快指针或慢指针其中一个回到头结点,然后双方速度都变为每次走一步,再次相交,则到达环点(相当于慢指针走了一整个流程,快指针先走一个流程,再走半个流程)

  /**
     * 返回当前链表的环点
     *
     * @param head
     * @return
     */
    private static Node getLoopNode(Node head) {
        if (head == null || head.next == null || head.next.next == null) {
            return null;
        }
        Node fast = head.next.next;
        Node slow = head.next;
        while (fast != slow) {
            if (fast.next == null || fast.next.next == null) {
                return null;
            }
            fast = fast.next.next;
            slow = slow.next;
        }
        //此时 fast == slow
        fast = head;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }

双方都无环时,如何找交点

  /**
     * 无环时找共同结点
     *
     * @param head1
     * @param head2
     * @return
     */
    private static Node noLoop(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        int count1 = 0;
        int count2 = 0;
        Node cur1 = head1;
        Node cur2 = head2;
        while (cur1 != null) {
            count1++;
            cur1 = cur1.next;
        }
        while (cur2 != null) {
            count2++;
            cur2 = cur2.next;
        }
        //如果尾结点不同,则直接无共同点
        if (cur1 != cur2) {
            return null;
        }

        //使长的为1  短的为2
        if (count2 > count1) {
            Node temp = head2;
            head2 = head1;
            head1 = temp;
        }
        //此时 head1为长的,head2为短的
        int time = Math.abs(count1 - count2);
        cur1 = head1;
        cur2 = head2;
        while (time > 0) {
            cur1 = cur1.next;
            time--;
        }
        //此时双方后半段的长度相同
        while (cur1 != cur2) {
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        //因为是无环,所以有尾结点
        //要么双方同时为null,要么双方同时为某结点
        return cur1;
    }

双方都有环时,找交点
先判断两个环点是否相等,如果相等,则说明在环点之前相遇过,思路和无环点一样。
如果两个环点不相等,则有两种情况,在环内两个链表以不同的位置如环,或者两个根本就不是一个环。
其中一个不动,另一个在本环绕一圈,看看是否能找到另一个环点,找到则返回交点,没找到则返回null,即双方根本不在同一个环内。

/**
     * 返回两个带环链表的第一个相交节点
     *
     * @param loop1
     * @param loop2
     * @return
     */
    private static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
        Node cur1 = null;
        Node cur2 = null;
        if (loop1 == loop2) {
            cur1 = head1;
            cur2 = head2;
            //当双方有同一个环点
            //则在之前可以看做为无环的,在之前必有交点
            int n = 0;
            while (cur1 != loop1) {
                n++;
                cur1 = cur1.next;
            }
            while (cur2 != loop2) {
                n--;
                cur2 = cur2.next;
            }
            //此时 n 即为 head1 和 head2 的长度之差
            cur1 = n > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            //cur1为较长,cur2较短
            n = Math.abs(n);
            while (n != 0) {
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } else {
            //如果双方是两个不同的环点
            //则判断双方在不在同一个环
            // 即在loop1中找一遍 看看能不能找到loop2
            cur1 = loop1.next;
            while (cur1 != loop1) {
                if (cur1 == loop2) {
                    //找到就返回cur1
                    return cur1;
                }
                cur1 = cur1.next;
            }
            //否则就返回null
            return null;
        }
    }

调用

  public static class Node {
        public int value;
        public Node next;

        public Node(int data) {
            this.value = data;
        }
    }

    public static Node getIntersectNode(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        //要么两个都有环,要么两个都无环
        //如果一个有环,则代表无止尽,另一个无环,代表有止尽,则代表不相交
        Node loop1 = getLoopNode(head1);
        Node loop2 = getLoopNode(head2);
        if (loop1 == null && loop2 == null) {
            return noLoop(head1, head2);
        }
        if (loop1 != null && loop2 != null) {
            return bothLoop(head1,loop1,head2, loop2);
        }
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值