1. 程式人生 > >java 判斷兩個單鏈表是否相交

java 判斷兩個單鏈表是否相交

文章目錄

題目

單鏈表可能有環,也可能無環。給定兩個單鏈表的頭節點 head1 和 head2, 這兩個連結串列可能相交,也可能不相交。請實現一個函式,如果兩個連結串列相交,請返回相交的第一個節點;如果不相交,返回 null 即可。

思考

連結串列分有環連結串列和無環連結串列,如果兩個連結串列存在相交,則只有兩種可能,兩個連結串列都無環或者都有環。

  1. 如果連結串列都無環,則先判斷連結串列的尾指標是否一樣,如果不一樣,則沒有相交。如果一樣,則找出兩個連結串列的長度差,將兩個連結串列從距離尾節點同樣的距離進行掃描,如果相交,則必然有一處掃描節點相同。例項資料List1:1->2->3->4->5->6->7->null,List2:0->9->8->6->7->null,第一個相交節點為6
    示例圖如下,

這裡寫圖片描述
2. 如果連結串列都有環,則只肯能有下面兩種情況(如下圖)。兩種情況區分的方法為:入環點是否相同。

如果相同則為第一種情況:那麼查詢第一個相交點與無環的單鏈表相交找第一個交點的方法一樣。

如果入環點不同,則為第二種情況,這個相交點或者為list1 的入環點loop1或者為list2的入環點loop2。

情況1例項資料(List1:1->2->3->4->5->6->7->4,List2:0->9->8->2->3->4->5->6->7->4,第一個交點為2)

情況2例項資料(List1:1->2->3->4->5->6->7->4,List2:0->9->8->6->7->4->5->6,第一個交點為4或6)
這裡寫圖片描述

這裡寫圖片描述

原始碼

public class FindIntersect {
    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;
    }
    /*
     * 判斷是否存在環,如果存在,則找出環的入口點。
     * 入口點找法:快慢指標,塊指標走兩步,滿指標走一步,如果存在迴圈,則在慢指標走完環前,總會和快指標相遇。
     * 從頭指標和相遇點同時向後走,相遇的點必定是入口點。(下面數學推導)*/
    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) {
        if (head1 == null || head2 == null) {
            return null;
        }
        Node cur1 = head1;
        Node cur2 = head2;
        int n = 0;
        while (cur1.next != null) {
            n++;
            cur1 = cur1.next;
        }
        while (cur2.next != null) {
            n--;
            cur2 = cur2.next;
        }
        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;
        }
    }

    public static void main(String args[]){
    //側重演算法,沒有實現連結串列部分
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        Node node6 = new Node(6);
        Node node7 = new Node(7);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node6;
        node6.next = node7;
        node7.next = node4;
        Node node11 = new Node(0);
        Node node22 = new Node(9);
        Node node33 = new Node(8);
        node11.next = node22;
        node22.next = node33;
        node33.next = node6;

      Node result = getIntersectNode(node1,node11);
        System.out.print(result.value);
   //輸出4
    }
}

環的入口

當fast與slow相遇時,slow肯定沒有走遍歷完連結串列,而fast已經在環內迴圈了n圈(1<=n)。假設slow走了s步,則fast走了2s步(fast步數還等於s 加上在環上多轉的n圈),設環長為r,則:
2s = s + nr
s= nr
設整個連結串列長L,入口環與相遇點距離為x,起點到環入口點的距離為a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
(L – a – x)為相遇點到環入口點的距離,由此可知,從連結串列頭到環入口點等於(n-1)迴圈內環+相遇點到環入口點(從相遇點向後遍歷迴圈回到入口點的距離),於是我們從連結串列頭、與相遇點分別設一個指標,每次各走一步,兩個指標必定相遇,且相遇點為環入口點,也即為兩個連結串列的第一個相同節點。
參考:
http://blog.csdn.net/happymatilian/article/details/47811161
http://blog.csdn.net/linyunzju/article/details/7753548
http://blog.csdn.net/jiqiren007/article/details/6572685
http://blog.csdn.net/javaacmer/article/details/9004814