- 题目:给你一个长度为 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 作为传入参数。
示例:输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
- 哈希表保存新老节点映射,时间复杂度O(N),空间复杂度O(N)
public Node copyRandomList(Node head){
if (head == null)
return null;
// map储存key为老节点,value为新节点的映射
HashMap<Node, Node> map = new HashMap<>();
// 辅助节点pre帮助创建新链表
Node pre = new Node(-1);
Node tail = pre;
// 从头遍历一遍链表
while (head != null){
// 如果说head节点在map已经存在,说明之前有某个节点的random指向它提前把它创建出来了,
// 那么新节点的next直接指向它就可以
if (map.containsKey(head)){
tail.next = map.get(head);
tail = tail.next;
// 如果不存在,就创建一个新节点把next指向他,并且把新节点和旧节点head的映射存入map
}else {
tail.next = new Node(head.val);
tail = tail.next;
map.put(head, tail);
}
// 如果head的random指向null,不用考虑其他情况,新节点的random也指向random
if (head.random == null){
tail.random = null;
}else {
// 不指向null,那要考虑random指向的节点此时有没有被创建出来
// 如果已经被创建,新random直接指过去
if (map.containsKey(head.random)) {
tail.random = map.get(head.random);
} else {
// 如果没创建,创建一个然后把random指过去
tail.random = new Node(head.random.val);
map.put(head.random, tail.random);
}
}
head = head.next;
}
return pre.next;
}
- 节点穿插,利用新旧节点之间的位置关系代替哈希表,完成random节点的寻找。
public Node copyRandomList(Node head) {
if (head == null)
return null;
Node cur = head;
Node next = null;
// 第一次遍历创建出新节点并穿插进旧链表中
while (cur != null){
next = cur.next;
cur.next = new Node(cur.val);
cur.next.next = next;
cur = next;
}
cur = head;
Node curCopy = null;
// 第二次循环要把新节点的random指向旧节点对应random的next位置(就是应该指向的地方)
while (cur != null){
next = cur.next.next;
curCopy = cur.next;
curCopy.random = cur.random != null ? cur.random.next : null;
cur = next;
}
Node res = head.next;
cur = head;
// 第三次遍历把新节点剥离出来
while (cur != null){
next = cur.next.next;
curCopy = cur.next;
cur.next = next;
curCopy.next = next != null ? next.next : null;
cur = next;
}
return res;
}