1. 程式人生 > 實用技巧 >劍指Offer_#35_複雜連結串列的複製

劍指Offer_#35_複雜連結串列的複製

劍指Offer_#35_複雜連結串列的複製

劍指offer

Contents

題目

請實現 copyRandomList 函式,複製一個複雜連結串列。在複雜連結串列中,每個節點除了有一個 next 指標指向下一個節點,還有一個 random 指標指向連結串列中的任意節點或者 null。
例如:

提示:

  • -10000 <= Node.val <= 10000
  • Node.random 為空(null)或指向連結串列中的節點。
  • 節點數目不超過 1000 。

思路分析

方法1:雜湊表

  1. 構建雜湊表
    沿著next指標,逐個遍歷舊連結串列節點,克隆出一個新連結串列節點。向雜湊表新增<舊節點,新節點>的鍵值對。
  2. 構造新連結串列
    從頭節點開始,遍歷舊連結串列節點,然後通過雜湊表獲得舊節點所對應的新節點,以及舊節點的next節點/random節點所對應的新節點。設定每個新節點的nextrandom

方法2:原地修改連結串列

  1. 在每個節點後克隆一個新節點
  2. 設定新節點的random指標,指向應該指向的位置
  3. 修改next指標,將拷貝的連結串列分離出來

圖解

解答

解答1:雜湊表

class
Solution
{ public Node copyRandomList(Node head) { HashMap<Node,Node> map = new HashMap<>(); Node cur = head; while(cur != null){ map.put(cur,new Node(cur.val)); 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); } }

複雜度分析

時間複雜度O(n)
空間複雜度O(n)

解答2:原地修改連結串列

class Solution {
    public Node copyRandomList(Node head) {
        CloneNodes(head);
        ConnectRandomNodes(head);
        return ReconnectNodes(head);
    } 
    //在每個原節點之後增加一個克隆的節點
    public void CloneNodes(Node head){
        Node cur = head;
        while(cur != null){
            Node cloned = new Node(cur.val);
            cloned.next = cur.next;
            cur.next = cloned;
            cur = cloned.next;
        }
    }
    //設定每個新節點的random指標
    public void ConnectRandomNodes(Node head){
        Node cur = head;
        while(cur != null){
            //當前節點的下一個節點是克隆節點
            Node cloned = cur.next;
            if(cur.random != null) cloned.random = cur.random.next;
            //每次迭代,指標移動兩個節點
            cur = cloned.next;
        }
    }
    //將拷貝的連結串列拆分出來
    public Node ReconnectNodes(Node head){
        //cur指標指向舊連結串列的節點
        Node cur = head;
        //ERROR:必須對cloneNode和cloneHead在if之外進行初始化,否則可能不被初始化
        //clonedNode是新連結串列的指標
        Node clonedNode = null;
        //clonedHead是新連結串列頭節點
        Node clonedHead = null;
        if(cur != null){
            clonedHead = cur.next;      
            clonedNode = clonedHead; 
            //cur指標先走一步,這樣才方便連線克隆的節點
            cur.next = clonedNode.next;
            cur = cur.next;
        }
        //cur非null,那麼cloneNode一定也非null
        while(cur != null){
            //連線到下個克隆節點
            clonedNode.next = cur.next;
            //更新cloneNode指標
            clonedNode = clonedNode.next;
            //連線到下箇舊連結串列節點
            cur.next = clonedNode.next;
            //更新cur指標
            cur = cur.next;
        }
        return clonedHead;
    }     
}

複雜度分析

時間複雜度O(n)
空間複雜度O(1)

TIP:避免連結串列迭代中的空指標異常

比較容易出現的問題是空指標異常,防止這個異常的方式就是增加一個條件判斷。具體舉例說明:

  • 因為要訪問cur.random.next,所以必須保證cur.random不是null
if(cur.random != null) cloned.random = cur.random.next;
  • 因為要訪問cur.next,所以必須保證cur不是null
if(cur != null){
            clonedHead = cur.next;      
            clonedNode = clonedHead; 
            //cur指標先走一步,這樣才方便連線克隆的節點
            cur.next = clonedNode.next;
            cur = cur.next;
        }

也就是說,每次些連結串列迴圈的程式碼,一定要注意迴圈過程中,*.next或者*.val之類的語句,一定要再其之前增加條件判斷語句if(* != null)