题目描述
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
分析
输入:链表头节点 head
输出:复制的链表的头节点
解题思路
这道题有两种解法:
哈希表 + 回溯
我们这么思考,如果是一个普通的链表,只需要从头到尾地遍历一遍,创建一个新链表,设置上复制的值就可以了,但是由于我们的链表节点有一个 random 属性,所以我们需要通过回溯的方法创建节点。
首先我们用一个 map 记录下来每个节点的创建情况,从头开始复制节点。
对于每个节点,我们先创建它,复制 val,并保存在 map,然后我们递归地进行 next 和 random 的复制。这样每次递归过程中,如果 map 里有这个节点,我们就直接返回它。
迭代 + 节点拆分
我们也可以通过迭代的方法去省掉哈希的空间,首先我们从头到尾遍历链表,没遍历到一个节点,我们做一次复制,声明为 newNode 作为当前遍历到节点的 next。注意,第一次遍历的时候可以先不复制 random,只复制出所有的节点即可。
第二次遍历我们把所有新复制出来的节点的 random 指向正确的位置。
第三次遍历我们做节点拆分,把复制的节点组成新的链表,返回它的 head。
代码
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function (head, map = new Map()) {
if (head === null) return null;
if (!map.has(head)) {
map.set(head, {
val: head.val,
});
// take care that we should firstly create one node before we go deeper, or there never gonna be one node created
Object.assign(map.get(head), {
next: copyRandomList(head.next, map),
random: copyRandomList(head.random, map),
});
}
return map.get(head);
};
/**
* // Definition for a Node.
* function Node(val, next, random) {
* this.val = val;
* this.next = next;
* this.random = random;
* };
*/
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function (head) {
if (!head) return null;
// copy node val
for (let node = head; node !== null; node = node.next.next) {
const newNode = new Node(node.val, node.next, null);
node.next = newNode;
}
// copy node random
for (let node = head; node !== null; node = node.next.next) {
const newNode = node.next;
newNode.random = node.random && node.random.next;
}
let newHead = head.next;
// unlink
for (let node = head; node !== null; node = node.next) {
const newNode = node.next;
node.next = node.next.next;
newNode.next = (newNode.next && newNode.next.next) || null;
}
return newHead;
};
复杂度
时间:O(N),遍历所有节点几遍
空间:O(N) for 哈希,O(1) for 迭代