1. 程式人生 > 其它 >LeetCode-138. 複製帶隨機指標的連結串列

LeetCode-138. 複製帶隨機指標的連結串列

題目來源

138. 複製帶隨機指標的連結串列

題目詳情

給你一個長度為 n 的連結串列,每個節點包含一個額外增加的隨機指標 random ,該指標可以指向連結串列中的任何節點或空節點。

構造這個連結串列的深拷貝。深拷貝應該正好由 n全新 節點組成,其中每個新節點的值都設為其對應的原節點的值。新節點的 next 指標和 random 指標也都應指向複製連結串列中的新節點,並使原連結串列和複製連結串列中的這些指標能夠表示相同的連結串列狀態。複製連結串列中的指標都不應指向原連結串列中的節點

例如,如果原連結串列中有 XY 兩個節點,其中 X.random --> Y 。那麼在複製連結串列中對應的兩個節點 x

y ,同樣有 x.random --> y

返回複製連結串列的頭節點。

用一個由n個節點組成的連結串列來表示輸入/輸出中的連結串列。每個節點用一個[val, random_index]表示:

  • val:一個表示Node.val的整數。
  • random_index:隨機指標指向的節點索引(範圍從0n-1);如果不指向任何節點,則為null

你的程式碼 接受原連結串列的頭節點 head 作為傳入引數。

示例 1:

輸入: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
輸出: [[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

輸入: head = [[1,1],[2,1]]
輸出: [[1,1],[2,1]]

示例 3:

輸入: head = [[3,null],[3,0],[3,null]]
輸出: [[3,null],[3,0],[3,null]]

提示:

  • 0 <= n <= 1000
  • -104<= Node.val <= 104
  • Node.randomnull 或指向連結串列中的節點。

題解分析

解法一:遞迴法

  1. 本題一開始看沒有特別清晰的思路,如果只有next指標那題目還簡單,但是本題多了一個隨機指標,那問題就變得複雜了,因為如果使用迭代法,有可能隨機指標指向的節點還沒有遍歷到。
  2. 考慮到使用迭代法無法拷貝還未遍歷到的節點,所以這裡可以藉助遞迴的思想,遞迴拷貝next指標和random指標。
  3. 此外,因為隨機指標是隨機指向任意節點的,為了防止出現遞迴過程中出現棧溢位以及重複遍歷已經遍歷的節點,這裡還需要使用一個HashMap來儲存對應節點的複製節點。
/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

class Solution {
    HashMap<Node, Node> map = new HashMap<>();
    public Node copyRandomList(Node head) {
        return copy(head);
    }

    private Node copy(Node now){
        if(now == null){
            return null;
        }
        // 已經訪問過now節點了
        if(map.containsKey(now)){
            return map.get(now);
        }
        Node news = new Node(now.val);
        map.put(now, news);
        news.next = copy(now.next);
        news.random = copy(now.random);
        return news;
    }
}

解法二:迭代法

題目要求我們複製一個長度為 n 的連結串列,該連結串列除了每個節點有一個指標指向下一個節點外,還有一個額外的指標指向連結串列中的任意節點或者 null,如下圖所示:

如何去複製一個帶隨機指標的連結串列?

首先我們可以忽略 random 指標,然後對原連結串列的每個節點進行復制,並追加到原節點的後面,而後複製 random 指標。最後我們把原連結串列和複製連結串列拆分出來,並將原連結串列復原。

圖示過程如下:

1、在每個節點的後面加上它的複製,並將原連結串列和複製連結串列連在一起。

2、 從前往後遍歷每一個原連結串列節點,對於有 random 指標的節點 p,我們讓它的 p->next->random = p->random->next,這樣我們就完成了對原連結串列 random 指標的復刻。

3、最後我們把原連結串列和複製連結串列拆分出來,並將原連結串列復原。

具體過程如下:

  • 1、定義一個 p 指標,遍歷整個連結串列,複製每個節點,並將原連結串列和複製連結串列連在一起。
  • 2、再次遍歷整個連結串列,執行 p->next->random = p->random->next,複製 random 指標。
  • 3、定義虛擬頭節點 dummy 用來指向複製連結串列的頭節點, 將兩個連結串列拆分並復原原連結串列。
/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

class Solution {
    HashMap<Node, Node> map = new HashMap<>();
    public Node copyRandomList(Node head) {
        // 複製每個節點,包括next指標
        for(Node p = head; p != null; p=p.next.next){
            Node news = new Node(p.val);
            news.next = p.next;
            p.next = news;
        }

        // 複製random指標
        for(Node p = head; p != null; p=p.next.next){
            if(p.random != null){
                p.next.random = p.random.next;
            }
        }

        // 拆分連結串列
        Node dumy = new Node(-1), cur = dumy;
        for(Node p = head; p != null; p = p.next){// 這裡的遍歷方式與前兩次的不同
            Node q = p.next;
            cur.next = q;
            cur = cur.next;
            p.next = q.next;
        }

        return dumy.next;
    }
}

結果展示

Either Excellent or Rusty