一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情
给你一个长度为
n的链表,每个节点包含一个额外增加的随机指针random,该指针可以指向链表中的任何节点或空节点。构造这个链表的 深拷贝。
看完题目,首先要注意的就是“深拷贝”,可以先回顾一下js里对象的深拷贝是怎么样的?
-
- 遍历对象的
key,判断当前属性值的类型,
- 简单类型直接复制(赋值),
- 对象类型(暂不考虑函数,Date等特殊对象):遍历该对象,对该对象进行拷贝(一般是递归调用deepClone方法)
- 遍历对象的
-
- 假设如下场景
var oa = { aname: 'a', c: oc }
var ob = { a: oa, bname: 'b' }
var oc = { cname: 'c', b: ob }
const obj = {
name: '要复制的对象',
cycle: oa
}
复制obj
- 对于
name属性,直接赋值obj[name]即可; cycle属性,它是一个对象,遍历他的属性进行深拷贝,发现cycle.c也是一个对象,那么对cycle.c也需要深度拷贝,然后cycle.c.b,然后cycle.c.b.a。。。然后死机了,如下图:
这就是深拷贝中循环引用的问题,
解决方式可以用下图表示
核心就是:通过cache去缓存之前的对象,然后优先使用cache中的对象。
这道题的解法中的解决循环引用的思路跟对象深拷贝是一致的。这道题的解法也主要依靠这个:
在创建节点之前,先去缓存中查看是否已创建:
- 已创建: 直接获取使用
- 没有创建:创建新节点,然后加入缓存
function copyRandomList(head: Node | null): Node | null {
if (head === null) return null
let duplication: Node | null = null
// 缓存
const map = new WeakMap()
let cur = head
while(cur) {
let node
if (!(node = map.get(cur))) {
// 创建节点之前 先去查看cache中是否存在,有的话直接使用
// 如果不存在新建,同时设置缓存
node = new Node(cur.val)
map.set(cur, node)
}
// 作为复制链表的头节点
if (duplication === null){
duplication = node
}
if (cur.random) {
// 对于random的处理一样也需要查看cache
let random
if (!(random = map.get(cur.random))) {
map.set(cur.random, (random = new Node(cur.random.val)))
}
node.random = random
}
if (cur.next) {
let next
// next节点 同理
if (!(next = map.get(cur.next))) {
map.set(cur.next, (next = new Node(cur.next.val)))
}
node.next = next
}
cur = cur.next
}
return duplication
};
- 时间复杂度: O(n)
- 空间复杂度: O(n)