鏈表的復制


問題描述

這是從微博上看到的一個面試問題,描述如下:

給一個鏈表,如下定義:

1 struct Node
2 {
3    struct Node* next;
4    struct Node* random;
5    void*data;
6 };

 

 其中random 指向鏈表中的任意一個節點或為空。

 

 現在要求對一個這種鏈表進行深度復制(即復制得到的鏈表中節點的next, random指向新鏈表中的相對應位置)。

 如下圖, 第一個是原鏈表,第二個是復制后的鏈表,現在要求盡可能快、省地完成這個復制過程。

簡單分析

這個問題的難點顯然就在於怎么設置新鏈表節點的random指針。

一種比較直觀的解法是類似於深度優先進行復制:

1) 復制節點A1,得到B1

2) 復制節點A1的random指向的節點NA1.

3) A1 = A1->next。

4) 如果A1不為空,重復以上動作。

但是上面的做法有問題,因為random是隨意指向鏈表中的節點,復制random與復制next的過程有重復,而上面的做法卻沒有檢查哪些節點是已經復制過的了。

為解決這個問題,我們可以在復制的過程中引入一個hash,用來保存哪些節點已經被復制過了。

1) 檢查hash,如果A1不在表中,對A1進行復制,得到B1, 並把A1放進hash表。

2) 檢查A1的random指向的節點NA1是否在hash中,如果NA1不在表中,對NA1進行復制,到NB1, 並把NA1放入hash表中。

3) A1 = A1->next;

4) 如果A1不為空,重復以上動作。

以上做法如果忽視檢查hash的時間消耗的話,時間復雜度為O(n), 但是空間復雜度也是O(n).

這個解決也可以接受了,也是比較容易想到的。

巧解

但事實還有一個比較巧的做法,細想一下,設置random的關鍵是新鏈表節點與舊鏈表節點之間要能建立起對應的關系。

這個關系應該怎樣建呢? 請看下圖

如圖,藍色節點是舊鏈表中的節點,紫色節點是新復制的節點,紅色線條是舊節點中的next,綠色線條是新節點中的next,藍色線條是新節點的random,黑色線條是舊節點的random。

算法如下 :

1) 掃描舊鏈表,逐個節點復制,得到上圖中的長鏈表, 時間空間復雜度均為O(n)

2) 注意到上一步建立的新鏈表中,每個舊節點都指向了相對應的新節點。

     剩下的問題就是怎么設置新節點的rando,現在開始設置新節點的random,及設置好新舊節點的next.

     令A = A1;

     a) 找到舊節點A的random指向的NA

     b) B=A->next, B1是A1對應的新節點

     c) NB = NA->next.

     d) A->next = B->next;  B->random = NB; B->next = A->next->next;

     e) A = A->next

     f) 如果A不為空,重復以上動作。

 

這個解法比較巧妙,它的優點是時間復雜度為O(n), 且空間復雜度為O(1).

不好的地方嘛--這個解法需要修改原來的鏈表。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM