復(fù)雜鏈表的復(fù)制(題號(hào)35)

問題描述:

輸入一個(gè)復(fù)雜鏈表(每個(gè)節(jié)點(diǎn)中有節(jié)點(diǎn)值,以及兩個(gè)指針,一個(gè)指向下一個(gè)節(jié)點(diǎn),另一個(gè)特殊指針random指向一個(gè)隨機(jī)節(jié)點(diǎn)),請(qǐng)對(duì)此鏈表進(jìn)行深拷貝,并返回拷貝后的頭結(jié)點(diǎn)。

示例:

輸入:{1,2,3,4,5,3,5,#,2,#}

輸出:{1,2,3,4,5,3,5,#,2,#}

解析:我們將鏈表分為兩段,前半部分{1,2,3,4,5}ListNode,后半部分{3,5,#,2,#}是隨機(jī)指針域表示。

以上示例前半部分可以表示鏈表為的ListNode1->2->3->4->5

后半部分3,5,#,2,#分別表示為:1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null

解法:

  1. hash表輔助存儲(chǔ)法

    思路:遍歷原鏈表,在遍歷的過(guò)程中判斷原鏈表的當(dāng)前節(jié)點(diǎn)是否存在復(fù)制節(jié)點(diǎn),存在將復(fù)制鏈表的前序節(jié)點(diǎn)next指針指向該復(fù)制的節(jié)點(diǎn)(從hash表取出),不存在則以當(dāng)前節(jié)點(diǎn)的內(nèi)容創(chuàng)建新的復(fù)制節(jié)點(diǎn),復(fù)制鏈表的前序節(jié)點(diǎn)的next指針指向它,并將節(jié)點(diǎn)地址映射關(guān)系存儲(chǔ)進(jìn)hash表。其次判斷原鏈表的當(dāng)前節(jié)點(diǎn)的random指針指向的節(jié)點(diǎn)是否存在復(fù)制節(jié)點(diǎn),存在將復(fù)制鏈表的前序節(jié)點(diǎn)的next節(jié)點(diǎn)的random指針指向該復(fù)制的節(jié)點(diǎn)(從hash表取出),不存在則以當(dāng)前節(jié)點(diǎn)的random節(jié)點(diǎn)內(nèi)容創(chuàng)建新的復(fù)制節(jié)點(diǎn),復(fù)制鏈表的前序節(jié)點(diǎn)的next節(jié)點(diǎn)的random指針指向它,并將節(jié)點(diǎn)地址映射關(guān)系存儲(chǔ)進(jìn)hash表。原鏈表的當(dāng)前節(jié)點(diǎn)curr和復(fù)制鏈表的前序節(jié)點(diǎn)prev步進(jìn),循環(huán)以上的操作直到currnull

    時(shí)間復(fù)雜度O(n), 遍歷一次原鏈表的時(shí)間
    空間復(fù)雜度O(n), 哈希表使用的空間

    注意事項(xiàng):

    1. 題意為深拷貝,所以復(fù)制鏈表的節(jié)點(diǎn)不能有指向原鏈表同位置節(jié)點(diǎn)的指針
    2. 特例情況的處理,這里要為復(fù)制鏈表創(chuàng)建虛擬頭結(jié)點(diǎn)好為前序節(jié)點(diǎn)prev初始賦值
  2. 鏈表拼接、拆分

    思路:這種方法需要遍歷三次鏈表。定義一個(gè)游標(biāo)指針curr,指向原鏈表的頭結(jié)點(diǎn),第一次遍歷原鏈表,遍歷的過(guò)程中將經(jīng)過(guò)的節(jié)點(diǎn)復(fù)制并鏈接到被復(fù)制節(jié)點(diǎn)的后面:原節(jié)點(diǎn)->復(fù)制節(jié)點(diǎn)->原節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn),curr指針步進(jìn),待遍歷完整個(gè)鏈表,此時(shí)鏈表的節(jié)點(diǎn)數(shù)翻倍,一前一后包含原節(jié)點(diǎn)和復(fù)制出的新節(jié)點(diǎn)。將curr指針復(fù)位到頭結(jié)點(diǎn),第二次遍歷更新過(guò)后的鏈表,這次是將復(fù)制節(jié)點(diǎn)的random指針賦值,判斷當(dāng)前節(jié)點(diǎn)的random節(jié)點(diǎn)是否為空,如果不為空則將當(dāng)前節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)的random指針指向?yàn)楫?dāng)前節(jié)點(diǎn)的random節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)。來(lái)到第三次遍歷,此時(shí)跟第二次遍歷一樣,定義三個(gè)指針p1、p2和復(fù)制出的鏈表的頭結(jié)點(diǎn)指針newHeadp1指向當(dāng)前鏈表的頭節(jié)點(diǎn),p2newHead指向頭結(jié)點(diǎn)的后續(xù)節(jié)點(diǎn),p1、p2的后續(xù)節(jié)點(diǎn)指向?yàn)樗鼈兒罄m(xù)節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn),完后分別步進(jìn)到新的后續(xù)節(jié)點(diǎn),當(dāng)p2的后續(xù)節(jié)點(diǎn)不為空的時(shí)候如此往復(fù)。待遍歷完后將p1的后續(xù)節(jié)點(diǎn)指向空(null)完成拆分,返回復(fù)制鏈表的頭結(jié)點(diǎn)newHead

    時(shí)間復(fù)雜度O(n), 遍歷了三次鏈表
    空間復(fù)雜度O(1), 使用了常數(shù)個(gè)鏈表指針變量

    注意事項(xiàng):

    1. 第三次遍歷完后注意將鏈接原鏈表的游標(biāo)指針指向null

解法一

圖示:

image

代碼

public class Solution {

    public RandomListNode Clone(RandomListNode pHead) {
        // 原鏈表的當(dāng)前節(jié)點(diǎn)初始賦值
        RandomListNode curr = pHead;
        // 偽造一個(gè)頭節(jié)點(diǎn),避免處理特例情況
        RandomListNode dummyHead = new RandomListNode(Integer.MIN_VALUE);
        // 前序節(jié)點(diǎn)初始賦值
        RandomListNode prev = dummyHead;
        // 創(chuàng)建一個(gè)hash表,用于存儲(chǔ)原節(jié)點(diǎn)和復(fù)制節(jié)點(diǎn)的映射關(guān)系,例如A->A'
        Map<RandomListNode, RandomListNode> map = new HashMap<>();
        // 遍歷原鏈表
        while (curr != null) {
            // 如果hash表中不存在原鏈表當(dāng)前節(jié)點(diǎn)的復(fù)制節(jié)點(diǎn)則以當(dāng)前節(jié)點(diǎn)的內(nèi)容創(chuàng)建新的復(fù)制節(jié)點(diǎn),復(fù)制鏈表的前序節(jié)點(diǎn)的next指針指向它,并將節(jié)點(diǎn)地址映射關(guān)系存儲(chǔ)進(jìn)hash表
            if (!map.containsKey(curr)) {
                prev.next = new RandomListNode(curr.val);
                map.put(curr, prev.next);
            } else {// 如果hash表中存在原鏈表當(dāng)前節(jié)點(diǎn)的復(fù)制節(jié)點(diǎn),則復(fù)制鏈表的前序節(jié)點(diǎn)的next指針指向該復(fù)制節(jié)點(diǎn)
                prev.next = map.get(curr);
            }
            // 如果hash表中不存在原鏈表當(dāng)前節(jié)點(diǎn)的random指針指向節(jié)點(diǎn)的復(fù)制節(jié)點(diǎn)則以當(dāng)前節(jié)點(diǎn)的random節(jié)點(diǎn)內(nèi)容創(chuàng)建新的復(fù)制節(jié)點(diǎn),復(fù)制鏈表的前序節(jié)點(diǎn)的next節(jié)點(diǎn)的random指針指向它,并將節(jié)點(diǎn)地址映射關(guān)系存儲(chǔ)進(jìn)hash表
            if (!map.containsKey(curr.random) && curr.random != null) {
                prev.next.random = new RandomListNode(curr.random.val);
                map.put(curr.random, prev.next.random);
            } else {// 如果hash表中存在原鏈表當(dāng)前節(jié)點(diǎn)的random節(jié)點(diǎn)的復(fù)制節(jié)點(diǎn),則復(fù)制鏈表的前序節(jié)點(diǎn)的next節(jié)點(diǎn)的random指針指向該復(fù)制節(jié)點(diǎn)
                prev.next.random = map.get(curr.random);
            }
            // 步進(jìn)原鏈表的當(dāng)前節(jié)點(diǎn)
            curr = curr.next;
            // 步進(jìn)復(fù)制鏈表的前序節(jié)點(diǎn)
            prev = prev.next;
        }
        // 返回復(fù)制鏈表的真實(shí)頭結(jié)點(diǎn)
        return dummyHead.next;
    }
}

解法二

圖示

image

代碼

/*
class RandomListNode {
    RandomListNode(int val) {
        this.val = val;
    }

    int val;
    RandomListNode next;
    RandomListNode random;
}
*/
public class Solution {

    public RandomListNode copyRandomList(RandomListNode pHead) {
        if (pHead == null) {
            return null;
        }
        RandomListNode curr = pHead;
        while (curr != null) {
            RandomListNode newNode = new RandomListNode(curr.val);
            newNode.next = curr.next;
            curr.next = newNode;
            curr = newNode.next;
        }

        curr = pHead;
        while (curr != null) {
            if (curr.random != null) {
                curr.next.random = curr.random.next;
            }
            curr = curr.next.next;
        }

        RandomListNode newHead = pHead.next;
        RandomListNode p1 = pHead;
        RandomListNode p2 = p1.next;
        while (p2.next != null) {
            p1.next = p1.next.next;
            p2.next = p2.next.next;
            p1 = p1.next;
            p2 = p2.next;
        }
        // 注意斷開原鏈表最末節(jié)點(diǎn)和新鏈表最末節(jié)點(diǎn)的連接
        p1.next = null;
        return newHead;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容