携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情
每日刷题 2022.08.21
- leetcode原题链接:leetcode.cn/problems/fu…
- 难度:中等
- 方法: 原地修改/map
题目
- 请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
示例
- 示例1
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
- 示例2
输入: head = [[1,1],[2,1]]
输出: [[1,1],[2,1]]
- 示例3
输入: head = [[3,null],[3,0],[3,null]]
输出: [[3,null],[3,0],[3,null]]
- 示例4
输入: head = []
输出: []
解释: 给定的链表为空(空指针),因此返回 null。
提示
-10000 <= Node.val <= 10000Node.random为空(null)或指向链表中的节点。- 节点数目不超过 1000 。
解题思路
- 根据题目中描述的可知:复杂的链表中的每一个节点都多了一个
random属性。需要完全的复制复杂链表,返回复制后的链表头节点。
Map映射
- 如果不考虑
random属性,就可以直接从原始链表的头节点开始依次往后拷贝一份,成为新的链表。 - 那么此时拥有
random属性,就可以使用map将原始链表的节点和新创建的链表的节点做一个映射,存储结构为key:原始链表的节点,value:新创建的链表的节点。注:map中存储的是节点的地址,因此如果节点的内容被修改,其也会跟着改变。 - 再次遍历原始链表,根据原始链表的属性
random,在map集合中获取到对应的新链表中的节点val。将当前节点的random赋值为val即可。 - 时间复杂度:
o(n),空间复杂度:o(n)
原地修改
- 原地修改的操作,并没有对于使用
map的解决方法,进行时间上的优化。而是在不使用额外的空间上做了优化。 - 第一步:对原始链表的每一个节点都新创建一份,并连接在新创建的节点的后方。
- 第二步:那么由此可见,偶数一定是原始链表中的节点;奇数一定是新创建的链表中的节点。也就是说当我们在查找原始链表中的第
i个节点的时候,其random属性所指向的节点的下一个节点,就是新创建的链表中的random属性所指向的节点。 - 第三步:将原始链表和新创建的链表分开。注意:这里一定要将原始的链表变回原本的样子,否则会报错!
- 时间复杂度:
o(n),空间复杂度:o(1)
AC代码
- map映射
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function(head) {
// 哈希表:记录原节点和现节点的映射关系
let map = new Map();
if(head === null) return null;
let root = head, dummy = new Node(), d = dummy;
while(root != null) {
let cur = new Node(root.val);
// 记录原节点和现在的节点之间的映射关系
map.set(root, cur);
d.next = cur;
d = d.next;
root = root.next;
}
root = head, d = dummy.next;
while(root != null) {
let key = root.random, val = map.get(key);
d.random = val;
root = root.next;
d = d.next;
}
return dummy.next;
};
- 原地修改
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function(head) {
// 原地算法
// 1. 将新创建的链表和原始的链表连接在一起:因为原始的节点后面一个是当前的节点
let h = head, nxt;
if(h === null) return null;
while(h != null) {
let cur = new Node(h.val);
nxt = h.next;
h.next = cur;
cur.next = nxt;
h = nxt;
}
// 2. 查找random的时候,直接查找其后面一个元素即可
// 偶数是原始节点,奇数是新的节点
h = head, nxt;
while(h != null) {
let neww = h.random === null ? null : h.random.next;
h.next.random = neww;
h = h.next.next;
}
// 3. 将原始的链表和新的链表拆解开
let d = new Node(0), dummy = d;
// d = d.next;
h = head, nxt;
while(h != null) {
let tt = h.next.next, mm = h.next;
h.next = tt;
d.next = mm;
d = d.next;
h = h.next;
}
// 将就列表恢复原样
return dummy.next;
};