题目:
题目链接:
题解:
方法一:哈希表
需要两次遍历链表,第一次链表将复制的节点与被复制的节点通过map结构一一对应起来,这时候只需要复制节点的val。第二次链表遍历,通过map结构,可以很快找到next和random对应的节点。
var copyRandomList = function(head) {
if(!head){
return head
}
let cur = head
let map = new Map()
// 第一次遍历,生成一个具有val属性的链表
while(cur){
map.set(cur,new _Node(cur.val,null,null))
cur = cur.next
}
cur = head
//第二次遍历,根据map映射关系,将random和next指针指向对应的节点或者null;
while(cur){
map.get(cur).next = map.get(cur.next) || null
map.get(cur).random = map.get(cur.random) || null
cur = cur.next
}
return map.get(head)
};
方法二:节点拆分
我们首先将该链表中每一个节点拆分为两个相连的节点,例如对于链表 A→B→C,我们可以将其拆分为 A→A ′ →B→B ′ →C→C ′。对于任意一个原节点 S,其拷贝节点 S ′即为其后继节点。
这样,我们可以直接找到每一个拷贝节点 S ′的随机指针应当指向的节点,即为其原节点 S 的随机指针指向的节点 T 的后继节点 T ′。需要注意原节点的随机指针可能为空,我们需要特别判断这种情况。
当我们完成了拷贝节点的随机指针的赋值,我们只需要将这个链表按照原节点与拷贝节点的种类进行拆分即可,只需要遍历一次。同样需要注意最后一个拷贝节点的后继节点为空,我们需要特别判断这种情况。
代码如下:
var copyRandomList = function(head) {
if(!head){
return head
}
let cur = head
//遍历链表,每一步都生成克隆结点,插在原结点中间
while(cur){
let newNode = new _Node(cur.val,cur.next,null)
cur.next = newNode
cur = cur.next.next
}
cur = head
//再遍历一次,`一对一对`地设置克隆结点的 random: `原结点`的 random 的 next, 就是`克隆结点`的 random 结点
while(cur){
let next = cur.next
next.random = (cur.random == null ? null: cur.random.next)
cur = cur.next.next
}
cur = head
// 记录复制后的链表的头结点
head = head.next
//用一个新指针指向克隆结点的头,把原结点的 next 和克隆结点的 next 拆开,返回新指针
while(cur){
let next = cur.next
cur.next = cur.next.next
next.next = (next.next !== null) ? next.next.next :null
cur = cur.next
}
return head
};
踩坑:
这里我想着把后面两次循环合并成一个循环,我的思路:将复制节点的next和random找到后就直接将原链表和复制链表的一部分复原,这样就不需要再开一层循环去复原链表。
代码如下:
var copyRandomList = function(head) {
if(!head){
return head
}
let cur = head
while(cur){
let newNode = new _Node(cur.val,cur.next,null)
cur.next = newNode
cur = cur.next.next
}
cur = head
head = head.next
// 错误思路:将两次循环做的工作放在一个循环里面
while(cur){
let next = cur.next
next.random = (cur.random == null ? null: cur.random.next)
//复原原节点
cur.next = next.next
cur = cur.next
// 复原复制节点
next.next = (next.next == null ? null: next.next.next)
//本来我们可以通过,cur.random.next去查找复制节点random对应的节点
//这种复原导致我们无法再通过cur.random.next去查找复制节点random对应的节点了,因为cur.random.next指向的是下一个原节点而不是复制节点
}
return head
};
但其实是不行的,前面遍历过的部分链表复原的话会断掉前面部分的原节点与复制节点的联系(即无法通过原节点找到对应的复制节点),这样后面的节点的random可能指向前面的节点,你前面的节点如果复原的话,就无法通过原节点找到其对应的复制节点了,就会导致random的指向是错误的