复制带随机指针的链表

151 阅读3分钟

要求

一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。 random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。 代码 接受原链表的头节点 head 作为传入参数。

思路

题目描述如下图

4.png

那我们可以先不管混乱的random指针

即下图

2.png

既然题目要求是深拷贝,那我们可以在原节点后面都加上一个“拷贝节点”(根据next指针排序)

即下图

3.png 此时,需要注意节点的random指向,因为我们是拷贝原节点的random,所以random目前指向原节点,需要让拷贝节点的random指向拷贝节点才行

即下图

5.png 需要变成下图

6.png 代码表现copy.random = copy.random.next

然后再把拷贝节点抽离出来,return拷贝节点的头即可

代码


var copyRandomList = function (head) {
    // 当传入的head为空,直接return出去
    if (!head) return null
    // 创建两个指针,pre指向head
    let pre = head,
        copy;
    // 加克隆节点,克隆原节点的所有数据,并且排在原节点后面,然后改变pre的指向
    while (pre) {
        copy = new Node(pre.val, pre.next, pre.random);
        pre.next = copy;
        pre = pre.next.next;
    }

    copy = head.next;
    // 改变克隆节点的random指向,然后copy指针继续寻找下一个克隆的节点,当找到最后一个,copy指针指向null(进入else)
    while (copy) {
        // 这是另一种写法
        // pre.random && (pre.random = pre.random.next);
        // (pre = pre.next) && (pre = pre.next);
        if (copy.random) {
            copy.random = copy.random.next;
        }
        if (copy.next) {
            copy = copy.next.next;
        } else {
            copy = copy.next;
        }
    }
    // 两指针都指向head.next(克隆节点),一个指针用于while循环的指向,一个指针用于return
    pre = copy = head.next;
    // 当克隆节点的下一节点(原节点/null)还有值(还未循环到最后的null)时
    // 抽离克隆节点成一个链表
    while (pre.next) {
        // head是原节点,原节点的指针指向下一个原节点
        // 注意,此处head不只代表传入的链表的第一个节点
        head.next = head.next.next;
        // pre是克隆节点,克隆节点指向下一个克隆节点
        pre.next = pre.next.next;
        // head指针指向下一个原节点
        head = head.next;
        // pre指针指向下一个克隆节点
        pre = pre.next;
    }
    // 断开最后一个原节点与克隆节点的next指向
    head.next = null;
    // 此时copy还指向第一个克隆节点,返回copy即可
    return copy;
};