一、题目描述
给你一个长度为 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
作为传入参数。
示例:
二、题解
题目分析:
给定的链表节点包含三个部分:
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
(1)val:当前节点存储的值
(2)next:指向下一节点
(3)random:随机指针 ,可以指向链表中的任何节点或空节点。
复制这个随机链表,即每个新节点的值与其对应的原节点的值相同。新节点的 next
指针和 random
指针也都指向对应的新节点
思路分析:
若复制普通链表,我们只需遍历原链表,在遍历过程中创建新节点,再通过前驱节点prev指向当前新节点,即可实现普通链表的复制。而本题中新增了 random,由于 random 可以指向链表中任意节点或空节点,意味着我们不仅要实现 前驱节点对当前节点的指向,也要实现 前驱节点对随机节点的指向
我们可以利用哈希表,通过构建原链表节点与新链表节点的键值对映射关系来解决该题
具体实现:
(1)根据原链表节点的值创建新链表,并把原链表的节点和创建的新链表节点放入哈希表 map 中
(2)遍历原链表,根据原链表节点cur的next、random找到新链表节点node的next、random指向,并确定新链表的next、random
通过原链表节点找到新链表节点:map.get(cur),新链表节点的next:map.get(cur).next
通过原链表节点的next找到新链表节点的next:map.get(cur.next)
确定新链表的next:map.get(cur).next = map.get(cur.next)
通过原链表节点找到新链表节点:map.get(cur),新链表节点的random:map.get(cur).random
通过原链表节点的next找到新链表节点的random:map.get(cur.random)
确定新链表的random:map.get(cur).random = map.get(cur.random)
(3)返回新链表的头节点
通过原链表的头节点,找到新链表的头节点,并返回
代码实现:
class Solution {
public Node copyRandomList(Node head) {
//根据原链表创建新链表
Node cur = head;
Map<Node,Node> map = new HashMap<>();
//遍历原链表,创建新链表,并将其放入map中
while(cur != null){
Node node = new Node(cur.val);
map.put(cur,node);
cur = cur.next;
}
//根据原链表节点的next、random修改新链表节点的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;
}
//返回新链表的头节点
return map.get(head);
}
}
题目来自:
138. 随机链表的复制 - 力扣(LeetCode)