兩個單鏈表相交的一系列問題

兩個單鏈表相交的一系列問題

【題目】在本題中,單鏈表可能有環,也可能無環。給定兩個單鏈表的頭節點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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章