LeetCode-138.Copy List with Random Pointer(含有隨機指標的連結串列的拷貝)(複雜連結串列的拷貝)
阿新 • • 發佈:2019-02-07
含有隨機指標的連結串列的拷貝(複雜連結串列的拷貝)
- 題意
- 方法一 : 使用HashMap儲存
- 方法二 : 方法一的另一種寫法
- 方法三 : O(1)的空間複雜度
題目連結
題意
給出的連結串列的結構:
class RandomListNode {
int label;
RandomListNode next, random;
RandomListNode(int x) { this.label = x; }
};
這個題目在劍指Offer中也出現過。
方法一 : 使用HashMap儲存
- 從左到右遍歷連結串列,對每個結點都複製生成相應的副本結點,然後將對應的關係(之前的結點和新的副本結點)放入雜湊表中;
- 然後從左到右設定每一個副本結點的next和random指標,即找到原先cur的next和random的拷貝(從Map中獲取);
- 最後返回副本結點的頭結點(map.get(head))即可;
看一個例子:
//普通的使用一個HashMap的額外空間為O(n)的方法
public RandomListNode copyRandomList(RandomListNode head) {
if(head == null)
return null;
HashMap<RandomListNode,RandomListNode> map = new HashMap<>();
RandomListNode cur = head;
while(cur != null){
map.put(cur,new RandomListNode(cur.label));
cur = cur.next;
}
cur = head;
while(cur != null){
map.get(cur).next = map.get(cur.next);
map. get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
方法二 : 方法一的另一種寫法
- 方法一的寫法是第一次儲存每個結點的時候沒有直接找到拷貝結點的next域結點;
- 這個方法是在拷貝原結點的時候,順便拷貝了結點的next域,拷貝完next域之後,最後就只要拷貝random域了;
- 注意這裡使用cur指向原連結串列的head,使用copyCur指向複製連結串列的head,然後這兩個指標同時完成的是兩個工作: a.先設定好副本拷貝結點的next域,然後將對應的原來連結串列的結點和拷貝的結點put進map,然後cur和copyCur都同時向後繼續移動一個位置;
//使用Map的另一種寫法,速度一般
public RandomListNode copyRandomList(RandomListNode head) {
if(head == null)
return null;
HashMap<RandomListNode,RandomListNode>map = new HashMap<>();
RandomListNode copyHead = new RandomListNode(head.label);
map.put(head,copyHead);
RandomListNode cur = head,copyCur = copyHead;
while(cur != null){
if(cur.next != null)
copyCur.next = new RandomListNode(cur.next.label);
map.put(cur.next,copyCur.next);
cur = cur.next;
copyCur = copyCur.next;
}
cur = head;
while(cur != null){
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return copyHead;
}
由於連結串列的天然的遞迴結構,也可以使用遞迴的寫法:
//上一種方法的遞迴的寫法
public RandomListNode copyRandomList(RandomListNode head) {
if(head == null)
return null;
HashMap<RandomListNode,RandomListNode>map = new HashMap<>();
RandomListNode copyHead = process(head,map);
RandomListNode cur = head;
while(cur != null){
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return copyHead;
}
// 巨集觀來看: 就是返回拷貝以node為頭結點的連結串列的拷貝 以及next的拷貝
private RandomListNode process(RandomListNode node, HashMap<RandomListNode,RandomListNode>map){
if(node == null){
return null;
}
RandomListNode copyNode = new RandomListNode(node.label);
map.put(node,copyNode);
copyNode.next = process(node.next,map);
return copyNode;
}
方法三 : O(1)的空間複雜度
這個方法是最好的解決辦法,分為三個步驟:
- 第一個步驟,先從左到右遍歷一遍連結串列,對每個結點cur都複製生成相應的副本結點copy,然後把副本結點copy放在cur和下一個要遍歷結點的中間;
- 再從左到右遍歷一遍連結串列,在遍歷時設定每一個結點的副本結點的random指標;
- 設定完random指標之後,將連結串列拆成兩個連結串列,返回第二個連結串列的頭部;
//O(1)的空間複雜度
public RandomListNode copyRandomList(RandomListNode head) {
if(head == null)
return null;
RandomListNode cur = head,next = null;
//先拷貝一份原來的連結串列
while(cur != null){
next = cur.next; //先存著之前的next
cur.next = new RandomListNode(cur.label);
cur.next.next = next;
cur = next;
}
//複製結點的random指標
cur = head;
RandomListNode copyCur = null;
while(cur != null){
next = cur.next.next; //儲存原來連結串列中的下一個
copyCur = cur.next; //複製連結串列的cur
copyCur.random = cur.random != null ? cur.random.next : null;
cur = next;
}
//拆開兩個連結串列
RandomListNode copyHead = head.next;
cur = head;
while(cur != null){
next = cur.next.next;
copyCur = cur.next;
cur.next = next;
copyCur.next = next != null ? next.next : null;
cur = next;
}
return copyHead;
}