数据结构与算法(Java) 31:两个单链表相交的一系列问题⭐⭐⭐⭐

题目 在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点 head1和head2,这两个链表可能相交,也可能不相交。请实现一个函数, 如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null 即可。
要求 如果链表1 的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外 空间复杂度请达到O(1)。
思路 考察3个知识点:(1)单链表是否有环;(2)两条单链表是否相交;(3)找到第一个相交点。
(1)给定单链表,返回入环的第一个节点。判断其是否有环的结果只有两种情况,即:有环、无环。
判断方法:设置两个指针slow和fast,遍历链表,其中slow一次走一个节点,fast一次走两个节点。若链表有环,则 slow和fast必然会相遇,若无环,则fast会遍历至链表结束,即节点为Null。若链表有环,需要求出入环的第一个节点。
求解方法:记录判断是否有环时,slow最后的位置,同时将fast重置到表头,两个指针同时往下遍历,且都是 一次走一个节点,二者第一次相遇的点即为入环的第一个节点。
(2)判断两条链表是否相交,一共有6种拓扑关系,返回相交的第一个节点:a.两条链表都无环且不相交;b.两条链表都无环且相交;c.一条链表无环,另一条链表有环;d.两条链表都有环且不相交;e.两条链表都有环且相交,且入环点相同;f.两条链表都有环且相交,且入环点不同。
a.两条链表都无环且不相交:没有相交点,返回null;
b.两条链表都无环且相交:遍历两条链表,同时记录下每条链表的长度,以及最后一个节点。若两条链表的最后一个节点不同,则没有相交点返回null,否则的话将较长的链表从表头开始裁去长出来的部分,然后同时再次遍历两条链表,返回二者的第一个相同的节点;
c.一条链表无环,另一条链表有环:没有相交点,返回null;

判断两条有环链表是否相交的方法:首先判断两条链表的入环点是否相同,相同的话则属于情况[e],否则的话属于情况[d]或[f]。区分[d]和[f]的方法:拿出一条链表从入环点开始遍历,若转了一圈又回到了入环点且没有遇到另一条链表的入环点,则属于情况[d],若在遍历过程中遇到了另一条链表的入环点,则属于情况[f]。
d.两条链表都有环且不相交:没有相交点,返回null;
e.两条链表都有环且相交,且入环点相同:求解方法同[b],不同之处在于将[b]中链表尾部的Null用链表入环点替代,即只遍历到入环点结束;
f.返回两条链表中的任意一个入环点都可以。

package algorithm.section4;

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

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

    public static Node getIntersectNode(Node head1, Node head2){
        if (head1 == null || head2 == null) return null;
        Node loop1 = getLoop(head1);
        Node loop2 = getLoop(head2);
        if (loop1 == null && loop2 == null)
            return noLoop(head1, head2);
        else if (loop1 != null && loop2 != null)
            return twoLoop(head1, loop1, head2, loop2);
        else return null;
    }

    public static Node getLoop(Node head){
        if (head == null || head.next == null || head.next.next == null)
            return null;

        Node slow = head.next;
        Node fast = head.next.next;
        while (fast != slow) {
            if (fast.next == null || fast.next.next == null) return null;
            slow = slow.next;
            fast = fast.next.next;
        }

        fast = head;
        while (fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }

    public static Node noLoop(Node head1, Node head2){
        if (head1 == null || head2 == null) return null;
        Node cur1 = head1;
        Node cur2 = head2;
        int len1 = 1;
        int len2 = 1;

        while (cur1.next != null){
            len1++;
            cur1 = cur1.next;
        }
        while (cur2.next != null){
            len2++;
            cur2 = cur2.next;
        }

        if (cur1 != cur2) return null;
        else {
            cur1 = head1;
            cur2 = head2;
            if (len1 > len2){
                for (int i = 0; i < len1 - len2; i++)
                    cur1 = cur1.next;
            } else if (len1 < len2){
                for (int i = 0; i < len2 - len1; i++)
                    cur2 = cur2.next;
            }
            while (cur1 != cur2){
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        }
    }

    public static Node twoLoop(Node head1, Node loop1, Node head2, Node loop2){
        if (loop1 == loop2) {
            Node cur1 = head1;
            Node cur2 = head2;
            int len1 = 1;
            int len2 = 1;

            while (cur1.next != loop1){
                cur1 = cur1.next;
                len1++;
            }
            while (cur2.next != loop2){
                cur2 = cur2.next;
                len2++;
            }

            cur1 = head1;
            cur2 = head2;
            if (len1 > len2){
                for (int i = 0; i < len1-len2; i++)
                    cur1 = cur1.next;
            } else if (len1 < len2){
                for (int i = 0; i < len2-len1; i++)
                    cur2 = cur2.next;
            }
            while (cur1 != cur2){
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        }

        Node cur = loop1.next;
        while (cur != loop2){
            cur = cur.next;
            if (cur == loop1) return null;
        }
        return loop1;
    }

    public static void main(String[] args){
        // 1->2->3->4->5->6->7->null
        Node head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);

        // 0->9->8->6->7->null
        Node head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next;

        System.out.println(getIntersectNode(head1, head2).value);

        // 1->2->3->4->5->6->7->4...
        head1.next.next.next.next.next.next.next = head1.next.next.next;  // 7->4

        // 0->9->8->2...
        head2.next.next.next = head1.next;

        System.out.println(getIntersectNode(head1, head2).value);

        head2.next.next.next = head1.next.next.next.next.next;  // 8->6

        System.out.println(getIntersectNode(head1, head2).value);

    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值