JS复制带随机指针的链表

722 阅读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 作为传入参数。

 

示例 1:

image.png

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

示例 2:

image.png

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

示例 3:

image.png

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

示例 4:

输入: head = []
输出: []
解释: 给定的链表为空(空指针),因此返回 null。

要求深拷贝链表,原则上不能够引用和指向原链表的任何节点,这才是问题的关键,也是这个问题的难点,其实单纯的深拷贝不难,关键在于链表的每个节点还包含了随机指针,这个随机指针指向链表本身某个节点,如果我们做深拷贝意味着,也需要深拷贝 random 节点,但是 random 节点的指向是有规则的,必须是和原链表指向是 拷贝前后的同一个节点。 通俗来说:原链表 random 指向 a 节点,那么深拷贝后的链表 random 指向 a 拷贝后的节点。

假设我们不考虑 random 节点的指向问题,单纯对链表做深拷贝,则有:

var copyRandomList = function(head) {
    const res = new Node(head.val, head.next, head.random)
    let p = head
    while(p.next) {
        p = p.next
        res.next = new Node(p.val, p.next, p.random)
    }
    return res
};

虽然每一个node是新的对象,但是 node 指向的next 和 random 都是原链表中的地址,并不能达到题目的要求。

所以主要的关键在于将 next 和 random 指向的节点,也做一组缓存。为什么是缓存而不是深拷贝呢?因为题意是 原链表 random 指向 a 节点,那么深拷贝后的链表 random 指向 a 拷贝后的节点,指向的是拷贝后链表中的节点,而不是新创建的节点。

所以有设置 Map 做 random 和 next 拷贝后的节点缓存,当遇到random指向的时候,先判断是否在缓存中,如果在直接指向缓存的节点而非新创建节点,这样就可以达到目的。

按照分析,我们可以采用 next 和 random 递归的方式去一步一步判断和寻找缓存节点,则有:

var handle = function (head, map) {
    if (head === null) {
        return null
    }
    if (!map.get(head)) {
        map.set(head, {val: head.val})
        Object.assign(map.get(head), {next: handle(head.next, map), random: handle(head.random, map)})
    }
    return map.get(head)
}

var copyRandomList = function(head) {
    let map = new Map()
    return handle(head, map)
};

以上 map 为缓存节点的数据。