两个单链表相交的一系列问题

两个单链表相交的一系列问题

【题目】在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点head1和head2,这两个链表可能相交,也可能不相交。请实现一个函数,如果两个链表相交,则返回相交的第一个节点;如果不相交,则返回null。

【要求】如果链表1的长度为N,链表2的长度为M,时间复杂度请达到O(N+M),额外空间复杂度请达到O(1)

思考

其实这道题目是一系列的问题,拆分下来如下
1. 判断单链表是否成环
2. 对于成环的单链表,找出第一个入环的点
3. 判断两个环是否相交

所以我们先一步一步的实现。

单链表是否成环

首先我们可以通过借助外部数据结构的方式来完成。
用Hash结构,遍历链表,如果hash表中没有值,那么将该节点放入hash表中。如果单链表成环,那么在遍历的过程中,肯定会遇到hash表中已存在节点的情况。

如果不借助外部数据结构,那么我们可以通过快慢双指针的方式来判断
一个快指针,一个慢指针,快指针一次走两步,慢指针一次走一步,如果链表成环,那么两个指针一定会相遇的

入环的第一个节点

第一种方式当然还是借助外部数据结构了
用Hash结构,遍历链表并放入hash表中,放入之前先判断是否存在,如果出现了第一个存在的节点,那么它就是第一个入环的节点了

第二种方式同样是使用快慢指针,但同时需要借助一个公式
如果链表成环,那么快慢指针一定会相遇,那么在相遇的时候,快指针回到head节点,此时慢节点到入环点的距离等于head节点到入环点的距离

相交情况

两个链表寻找相交点可以分为三种情况:
1. 两个链表都不成环,那么就直接判断最后一个节点,如果最后一个节点相同,那么就肯定相交了
2. 两个链表都成环,在入环之前相交
3. 两个链表都成环,在分别成环之后相交,那么第一个相交点可以看作各自的入环点

所以这里都需要分情况来判断的
第一种情况很好判断是否相交,那么怎么获取第一个交点呢? 我们可以通过计数的方式,这样可以得知哪一个链表更长以及连链表的长度差值N,然后再先将长链表移动N步,最后两个链表一起移动,那么相遇的那个地方就是第一个交点了
对于第二种情况,如果在入环之前就相交了,那么它们的入环点就是相同的;第三种情况,入环点则不同

实现

判断单链表是否成环

public class FindFirstIntersectNode {

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

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

    /**
     * 判断单链表是否成环
     * 借助外部数据结构 
     */
    public static boolean isLoopNode1(Node head) {
        HashSet<Node> hash = new HashSet<Node>();
        while(head != null) {
            if(hash.contains(head)) {
                return true;
            }
            hash.add(head);
            head = head.next;
        }
        return false;
    }

    /**
     * 通过快慢指针的方式判断单链表是否成环
     */
    public static boolean isLoopNode2(Node head) {
        if(head == null || head.next == null || head.next.next == null) {
            return false;
        }
        Node fast = head.next.next;
        Node slow = head.next;
        while(fast != slow) {
            if(fast.next == null || fast.next.next == null) {
                return false;
            }
            fast = fast.next.next;
            slow = slow.next;
        }
        return true;
    }

}

获取第一个入环的节点

public class FindFirstIntersectNode {

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

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

    public static Node getLoopNode(Node head) {
        HashSet<Node> hash = new HashSet<Node>();
        while(head != null) {
            if(hash.contains(head)) {
                return head;
            }
            hash.add(head);
            head = head.next;
        }
        return null;
    }

    public static Node getLoopNode2(Node head) {
        if (head == null || head.next == null || head.next.next == null) {
            return null;
        }
        Node n1 = head.next; // n1 -> slow
        Node n2 = head.next.next; // n2 -> fast
        while (n1 != n2) {
            if (n2.next == null || n2.next.next == null) {
                return null;
            }
            n2 = n2.next.next;
            n1 = n1.next;
        }
        n2 = head; // n2 -> walk again from head
        while (n1 != n2) {
            n1 = n1.next;
            n2 = n2.next;
        }
        return n1;
    }

}

终极判断

public class FindFirstIntersectNode {

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

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

    public static Node findFirstIntersectNode(Node head1, Node head2) {
        if(head1 == null || head2 == null) {
            return null;
        }
        Node loopNode1 = getLoopNode(head1);
        Node loopNode2 = getLoopNode(head2);

        // no loop
        if(loopNode1 == null && loopNode2 == null) {
            return noLoop(head1, head2);
        }

        // before loopNode
        if(loopNode1 != null && loopNode2 != null) {
            return loopNode1;
        }

        return null;
    }

    public static Node getLoopNode(Node head) {
        if (head == null || head.next == null || head.next.next == null) {
            return null;
        }
        Node n1 = head.next; // n1 -> slow
        Node n2 = head.next.next; // n2 -> fast
        while (n1 != n2) {
            if (n2.next == null || n2.next.next == null) {
                return null;
            }
            n2 = n2.next.next;
            n1 = n1.next;
        }
        n2 = head; // n2 -> walk again from head
        while (n1 != n2) {
            n1 = n1.next;
            n2 = n2.next;
        }
        return n1;
    }

    public static Node noLoop(Node head1, Node head2) {
        Node cur1 = head1;
        Node cur2 = head2;
        int n = 0;
        while(cur1 != null) {
            cur1 = cur1.next;
            n++;
        }
        while(cur2 != null) {
            cur2 = cur2.next;
            n--;
        }
        if(cur1 != cur2) {
            return null;
        }
        cur1 = n > 0 ? head1 : head2;
        cur2 = cur1 == head1 ? head2 : head1;
        n = Math.abs(n);
        while(n > 0) {
            n--;
            cur1 = cur1.next;
        }
        while(cur1 != cur2) {
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }

    public 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;
            }
            cur1 = n > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            n = Math.abs(n);
            while (n != 0) {
                n--;
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } else {
            cur1 = loop1.next;
            while (cur1 != loop1) {
                if (cur1 == loop2) {
                    return loop1;
                }
                cur1 = cur1.next;
            }
            return null;
        }

    }
}

跟我一起刷题吧

Java-Algorithm

猜你喜欢

转载自blog.csdn.net/u013435893/article/details/80582740