【劍指Offer學習】【面試題26:複雜連結串列的複製】
阿新 • • 發佈:2018-12-31
題目:請實現函式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);
}
}
執行結果:
注意:劃紅線部分是為了區分原連結串列和複製連結串列,具體還原見程式碼註釋。