LeetCode 力扣 138. 復制帶隨機指針的鏈表

題目描述(中等難度)

給一個鏈表挺份,返回復制后的鏈表褒翰。鏈表節(jié)點相對于普通的多了一個 random 指針,會隨機指向鏈表內的任意節(jié)點或者指向 null匀泊。

思路分析

這道題其實和 133 題 復制一個圖很類似优训,這里的話就是要解決的問題就是,當更新當前節(jié)點的 random 指針的時候各聘,如果 random 指向的是很后邊的節(jié)點揣非,但此時后邊的節(jié)點還沒有生成,那么我們該如何處理躲因。

133 題 一樣早敬,我們可以利用 HashMap 將節(jié)點提前生成并且保存起來,第二次遍歷到他的時候直接從 HashMap 里邊拿即可大脉。

這里的話就有兩種思路搞监,一種需要遍歷兩邊鏈表,一種只需要遍歷一遍镰矿。

2020.3.3 更新琐驴,leetcode 增加了樣例,之前沒有重復的數字所以 key 存的 val 秤标,現在有了重復數字绝淡,將 key 修改為 Node。此外 Node 的無參的構造函數也被去掉了抛杨,也需要修改够委。

解法一

首先利用 HashMap 來一個不用思考的代碼。

遍歷第一遍鏈表怖现,我們不考慮鏈表之間的相互關系,僅僅生成所有節(jié)點,然后把它存到 HashMap 中屈嗤,val 作為 key潘拨,Node 作為 value

遍歷第二遍鏈表饶号,將之前生成的節(jié)點取出來铁追,更新它們的 nextrandom 指針。

public Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }
    HashMap<Node, Node> map = new HashMap<>();
    Node h = head;
    while (h != null) {
        Node t = new Node(h.val); 
        map.put(h, t);
        h = h.next;
    }
    h = head;
    while (h != null) {
        if (h.next != null) {
            map.get(h).next = map.get(h.next);
        }
        if (h.random != null) {
            map.get(h).random = map.get(h.random);
        }
        h = h.next;
    }
    return map.get(head);
}

解法二

解法一雖然簡單易懂茫船,但還是有可以優(yōu)化的地方的琅束。我們可以只遍歷一次鏈表。

核心思想就是延遲更新它的 next算谈。

1 -> 2 -> 3

用 cur 指向已經生成的節(jié)點的末尾
1 -> 2   
     ^
     c

然后將 3 構造完成

最后將 2 的 next 指向 3
1 -> 2 -> 3  
     ^
     c
     
期間已經生成的節(jié)點存到 HashMap 中涩禀,第二次遇到的時候直接從 HashMap 中拿

看下代碼理解一下含義吧

public Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }
    HashMap<Node, Node> map = new HashMap<>();
    Node h = head;
    Node cur = new Node(-1); //空結點,dummy 節(jié)點然眼,為了方便頭結點計算
    while (h != null) {
        //判斷當前節(jié)點是否已經產生過
        if (!map.containsKey(h)) {
            Node t = new Node(h.val);
            map.put(h, t);
        }
        //得到當前節(jié)點去更新它的 random 指針
        Node next = map.get(h);
        if (h.random != null) {
            //判斷當前節(jié)點是否已經產生過
            if (!map.containsKey(h.random)) {
                next.random = new Node(h.random.val);
                map.put(h.random, next.random);
            } else {
                next.random = map.get(h.random);
            }

        }
        //將當前生成的節(jié)點接到 cur 的后邊
        cur.next = next;
        cur = cur.next;
        h = h.next;
    }
    return map.get(head);
}

解法三

上邊的兩種解法都用到了 HashMap 艾船,所以額外需要 O(n) 的空間復雜度。現在考慮不需要額外空間的方法高每。

主要參考了這里屿岂。主要解決的問題就是我們生成節(jié)點以后,當更新它的 random 的時候鲸匿,怎么找到之前生成的節(jié)點爷怀,前兩種解法用了 HashMap 全部存起來,這里的話可以利用原來的鏈表的指針域带欢。

主要需要三步运授。

  1. 生成所有的節(jié)點,并且分別插入到原有節(jié)點的后邊
  2. 更新插入節(jié)點的 random
  3. 將新舊節(jié)點分離開來

一圖勝千言洪囤,大家看一下下邊的圖吧徒坡。

代碼對應如下。

public Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }
    Node l1 = head;
    Node l2 = null;
    //生成所有的節(jié)點瘤缩,并且分別插入到原有節(jié)點的后邊
    while (l1 != null) {
        l2 = new Node(l1.val);
        l2.next = l1.next;
        l1.next = l2;
        l1 = l1.next.next;
    }
    //更新插入節(jié)點的 random
    l1 = head;
    while (l1 != null) {
        if (l1.random != null) {
            l1.next.random = l1.random.next;
        }
        l1 = l1.next.next;
    }

    l1 = head;
    Node l2_head = l1.next;
    //將新舊節(jié)點分離開來
    while (l1 != null) {
        l2 = l1.next;
        l1.next = l2.next;
        if (l2.next != null) {
            l2.next = l2.next.next;
        }
        l1 = l1.next;
    }
    return l2_head;
}

解法四

不利用額外的空間復雜度還有一種思路喇完,參考 這里

解法三利用原鏈表的 next 域把新生成的節(jié)點保存了起來剥啤。類似的锦溪,我們還可以利用原鏈表的 random 域把新生成的節(jié)點保存起來。

主要還是三個步驟府怯。

  1. 生成所有的節(jié)點刻诊,將它們保存到原鏈表的 random 域,同時利用新生成的節(jié)點的 next 域保存原鏈表的 random牺丙。
  2. 更新新生成節(jié)點的 random 指針则涯。
  3. 恢復原鏈表的 random 指針复局,同時更新新生成節(jié)點的 next 指針。

一圖勝千言粟判。

相應的代碼如下亿昏。

public Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }
    Node l1 = head;
    Node l2 = null;
    //生成所有的節(jié)點,講它們保存到原鏈表的 random 域档礁,
    //同時利用新生成的節(jié)點的 next 域保存原鏈表的 random角钩。
    while (l1 != null) {
        l2 = new Node(l1.val);
        l2.next = l1.random;
        l1.random = l2;
        l1 = l1.next;
    }
    l1 = head;
    //更新新生成節(jié)點的 random 指針。
    while (l1 != null) {
        l2 = l1.random;
        l2.random = l2.next != null ? l2.next.random : null;
        l1 = l1.next;
    }

    l1 = head;
    Node l2_head = l1.random;
    //恢復原鏈表的 random 指針呻澜,同時更新新生成節(jié)點的 next 指針递礼。
    while (l1 != null) {
        l2 = l1.random;
        l1.random = l2.next;
        l2.next = l1.next != null ? l1.next.random : null;
        l1 = l1.next;
    }
    return l2_head;
}

解法一、解法二是比較直接的想法羹幸,直接利用 HashMap 存儲之前的節(jié)點脊髓。解法三、解法四利用原有鏈表的指針睹欲,通過指來指去完成了賦值供炼。鏈表操作的核心思想就是,在改變某一個節(jié)點的指針域的時候窘疮,一定要把該節(jié)點的指針指向的節(jié)點用另一個指針保存起來袋哼,以免造成丟失。

更多詳細通俗題解詳見 leetcode.wang 闸衫。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末涛贯,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子蔚出,更是在濱河造成了極大的恐慌弟翘,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骄酗,死亡現場離奇詭異稀余,居然都是意外死亡,警方通過查閱死者的電腦和手機趋翻,發(fā)現死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門睛琳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踏烙,你說我怎么就攤上這事师骗。” “怎么了讨惩?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵辟癌,是天一觀的道長。 經常有香客問我荐捻,道長黍少,這世上最難降的妖魔是什么寡夹? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮仍侥,結果婚禮上要出,老公的妹妹穿的比我還像新娘鸳君。我一直安慰自己农渊,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布或颊。 她就那樣靜靜地躺著砸紊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪囱挑。 梳的紋絲不亂的頭發(fā)上醉顽,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音平挑,去河邊找鬼游添。 笑死,一個胖子當著我的面吹牛通熄,可吹牛的內容都是我干的唆涝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼唇辨,長吁一口氣:“原來是場噩夢啊……” “哼廊酣!你這毒婦竟也來了?” 一聲冷哼從身側響起赏枚,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤亡驰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后饿幅,有當地人在樹林里發(fā)現了一具尸體凡辱,經...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年栗恩,在試婚紗的時候發(fā)現自己被綠了透乾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡摄凡,死狀恐怖续徽,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情亲澡,我是刑警寧澤钦扭,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站床绪,受9級特大地震影響客情,放射性物質發(fā)生泄漏其弊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一膀斋、第九天 我趴在偏房一處隱蔽的房頂上張望梭伐。 院中可真熱鬧,春花似錦仰担、人聲如沸糊识。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赂苗。三九已至,卻和暖如春贮尉,著一層夾襖步出監(jiān)牢的瞬間拌滋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工猜谚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留败砂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓魏铅,卻偏偏與公主長得像昌犹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子沦零,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355