1. 程式人生 > >【劍指Offer學習】【面試題26:複雜連結串列的複製】

【劍指Offer學習】【面試題26:複雜連結串列的複製】

題目:請實現函式ComplexListNode clone(ComplexListNode head),複製一個複雜連結串列。在複雜連結串列中,每個結點除了有一個next 域指向下一個結點外,還有一個sibling 指向連結串列中的任意結點或者null。

結點結構定義:

public static class ComplexListNode {
    int value;
    ComplexListNode next;
    ComplexListNode sibling;
}

解題思路:

圖4.8 是一個含有5 個結點的複雜連結串列。圖中實線箭頭表示next 指標,虛線箭頭表示sibling 指標。為簡單起見,指向null 的指標沒有畫出。

這裡寫圖片描述

在不用輔助空間的情況下實現O(n)的時間效率。

第一步:仍然是根據原始連結串列的每個結點N 建立對應的N’。把N’連結在N的後面。圖4.8 的連結串列經過這一步之後的結構,如圖4.9 所示。

這裡寫圖片描述

第二步:設定複製出來的結點的sibling。假設原始連結串列上的N的sibling指向結點S,那麼其對應複製出來的N’是N的pext指向的結點,同樣S’也是S的next指向的結點。設定sibling之後的連結串列如圖4.10 所示。

這裡寫圖片描述

第三步:把這個長連結串列拆分成兩個連結串列。把奇數位置的結點用next .
連結起來就是原始連結串列,把偶數位置的結點用next 連結起來就是複製
出來的連結串列。圖4. 10 中的連結串列拆分之後的兩個連結串列如圖4.11 所示。

這裡寫圖片描述

程式碼實現:

public class Test26 {
    /**
     * 複雜連結串列結點
     */
    public static class ComplexListNode {
        int value;
        ComplexListNode next;
        ComplexListNode sibling;
    }

    /**
     * 實現函覆制一個複雜連結串列。在複雜連結串列中,每個結點除了有一個next欄位指向下一個結點外,
     * 還有一個sibling欄位指向連結串列中的任意結點或者NULL
     *
     * @param head 連結串列表頭結點
     * @return 複製結點的頭結點
     */
public static ComplexListNode clone(ComplexListNode head) { // 如果連結串列為空就直接返回空 if (head == null) { return null; } // 先複製結點 cloneNodes(head); // 再連結sibling欄位 connectNodes(head); // 將整個連結串列拆分,返回複製連結串列的頭結點 return reconnectNodes(head); } /** * 複製一個連結串列,並且將複製後的結點插入到被複制的結點後面,只連結複製結點的next欄位 * * @param head 待複製連結串列的頭結點 */ public static void cloneNodes(ComplexListNode head) { // 如果連結串列不空,進行復制操作 while (head != null) { // 建立一個新的結點 ComplexListNode tmp = new ComplexListNode(); // 將被複制結點的值傳給複製結點 // tmp.value = head.value; /////////////////////////////////////////////////////////////////////////////////////////// // TODO 此處為了做測試,讓複製結點的值都增加了100,如果不需要可以將下面一個行註釋掉,開啟上一行。 tmp.value = head.value + 100; /////////////////////////////////////////////////////////////////////////////////////////// // 複製結點的next指向下一個要被複制的結點 tmp.next = head.next; // 被複制結點的next指向複製結點 head.next = tmp; // 到些處就已經完成了一個結點的複製並且插入到被複制結點的後面 // heed指向下一個被複制結點的位置 head = tmp.next; } } /** * 設定複製結點的sibling欄位 * * @param head 連結串列的頭結 */ public static void connectNodes(ComplexListNode head) { // 如連結串列不為空 while (head != null) { // 當前處理的結點sibling欄位不為空,則要設定其複製結點的sibling欄位 if (head.sibling != null) { // 複製結點的sibling指向被複制結點的sibling欄位的下一個結點 // head.next:表求複製結點, // head.sibling:表示被複制結點的sibling所指向的結點, // 它的下一個結點就是它的複製結點 head.next.sibling = head.sibling.next; } // 指向下一個要處理的複製結點 head = head.next.next; } } /** * 剛複製結點和被複制結點拆開,還原被複制的連結串列,同時生成監製連結串列 * * @param head 連結串列的頭結點 * @return 複製連結串列的頭結點 */ public static ComplexListNode reconnectNodes(ComplexListNode head) { // 當連結串列為空就直接返回空 if (head == null) { return null; } // 用於記錄複製連結串列的頭結點 ComplexListNode newHead = head.next; // 用於記錄當前處理的複製結點 ComplexListNode pointer = newHead; // 被複制結點的next指向下一個被複制結點 head.next = newHead.next; // 指向新的被複制結點 head = head.next; while (head != null) { // pointer指向複製結點 pointer.next = head.next; pointer = pointer.next; // head的下一個指向複製結點的下一個結點,即原來連結串列的結點 head.next = pointer.next; // head指向下一個原來連結串列上的結點 head = pointer.next; } // 返回複製連結串列的頭結點 return newHead; } /** * 輸出連結串列資訊 * * @param head 連結串列頭結點 */ public static void printList(ComplexListNode head) { while (head != null) { System.out.print(head.value + "->"); head = head.next; } System.out.println("null"); } /** * 判斷兩個連結串列是否是同一個連結串列,不是值相同 * * @param h1 連結串列頭1 * @param h2 連結串列頭2 * @return true:兩個連結串列是同一個連結串列,false:不是 */ public static boolean isSame(ComplexListNode h1, ComplexListNode h2) { while (h1 != null && h2 != null) { if (h1 == h2) { h1 = h1.next; h2 = h2.next; } else { return false; } } return h1 == null && h2 == null; } public static void main(String[] args) { // ----------------- // \|/ | // 1-------2-------3-------4-------5 // | | /|\ /|\ // --------+-------- | // ------------------------- ComplexListNode head = new ComplexListNode(); head.value = 1; head.next = new ComplexListNode(); head.next.value = 2; head.next.next = new ComplexListNode(); head.next.next.value = 3; head.next.next.next = new ComplexListNode(); head.next.next.next.value = 4; head.next.next.next.next = new ComplexListNode(); head.next.next.next.next.value = 5; head.sibling = head.next.next; head.next.sibling = head.next.next.next.next.next; head.next.next.next.sibling = head.next; ComplexListNode tmp = head; printList(head); ComplexListNode newHead = clone(head); printList(head); System.out.println(isSame(head, tmp)); printList(newHead); System.out.println(isSame(head, newHead)); // 有指向自身的情況 // ----------------- // \|/ | // 1-------2-------3-------4-------5 // | | /|\ /|\ // | | -- | // |------------------------| ComplexListNode head2 = new ComplexListNode(); head2.value = 1; head2.next = new ComplexListNode(); head2.next.value = 2; head2.next.next = new ComplexListNode(); head2.next.next.value = 3; head2.next.next.next = new ComplexListNode(); head2.next.next.next.value = 4; head2.next.next.next.next = new ComplexListNode(); head2.next.next.next.next.value = 5; head2.next.sibling = head2.next.next.next.next; head2.next.next.next.sibling = head2.next.sibling; head2.next.next.sibling = head2.next.next; System.out.println("\n"); tmp = head2; printList(head2); ComplexListNode newHead2 = clone(head2); printList(head2); System.out.println(isSame(head2, tmp)); printList(newHead2); System.out.println(isSame(head2, newHead2)); ComplexListNode head3 = new ComplexListNode(); head3.value = 1; System.out.println("\n"); tmp = head3; printList(head3); ComplexListNode newHead3 = clone(head3); printList(head3); System.out.println(isSame(head3, tmp)); printList(newHead3); System.out.println(isSame(head3, newHead3)); System.out.println("\n"); ComplexListNode head4 = clone(null); printList(head4); } }

執行結果:

注意:劃紅線部分是為了區分原連結串列和複製連結串列,具體還原見程式碼註釋。

這裡寫圖片描述