Tag:List
Difficulty:Medium
Problem
复制带随机指针的链表
Medium
给你一个长度为 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 作为传入参数。
Solution
变化:多了一个random
解法一:
简单的解法,用map记录节点对应关系,然后遍历链表,修改next和random指向。具体步骤如下:
1 生成新老节点对应 map(新,老) 只存储节点内存地址,不考虑指针 -- 对应关系
2 遍历老链表,设置新链表的next random
3 最后返回新链表的头节点
这种解法速度快,容易实现,但用了额外的内存空间
public Node copyRandomList(Node head) {
if (head == null) {
return head;
}
Map<Node, Node> map = new HashMap<>();
Node cur = head;
// 1 生成新老节点对应 map(新,老) 只存储节点内存地址,不考虑指针 -- 对应关系
while (cur != null) {
map.put(cur, new Node(cur.val)); // 只存储节点内存地址,不考虑指针
cur = cur.next;
}
// 2 遍历老链表,设置新链表的next random
cur = head;
while (cur != null) {
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
// 3 最后返回新链表的头节点
return map.get(head);
}
解法二:
在当前链表后生成克隆节点,然后改变next和random指向并还原链表。具体步骤如下:
1 遍历链表,生成克隆节点放在当前节点后面
2 遍历链表,改变克隆节点的random
3 拆分,改变next 既还原了原链表,又克隆了新链表
public Node copyRandomList(Node head) {
if (head == null) {
return head;
}
// 1 遍历链表,生成克隆节点放在当前节点后面
Node cur = head, p = null;
while (cur != null) {
p = cur.next;
cur.next = new Node(cur.val); // 加在后面
cur.next.next = p;
cur = p;
}
// 2 遍历链表,改变克隆节点的random
cur = head;
while (cur != null) {
p = cur.next; // 克隆节点
p.random = cur.random == null ? null : cur.random.next;
cur = p.next;
}
// 3 拆分,改变next 既还原了原链表,又克隆了新链表
cur = head;
Node res = head.next;
Node curCopy = null;
while (cur != null) {
curCopy = cur.next;
p = cur.next.next;
cur.next = p;
curCopy.next = p == null ? null : p.next;
cur = p;
}
return res;
}
注意:这种方法使用的前提是链表可以修改