题目
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的深拷贝。
示例:
输入:
{" $ id":“1”,“next”:{" $ id":“2”,“next”:null,“random”:{" $ ref":“2”},“val”:2},“random”:{" $ ref":“2”},“val”:1}
解释:
<节点 1 的值是 1,它的下一个指针和随机指针都指向节点 2 。
节点 2 的值是 2,它的下一个指针指向 null,随机指针指向它自己。
提示:
你必须返回给定头的拷贝作为对克隆列表的引用。
解答
解法一:使用 map 存新旧结点映射
要点如下:
- 在新建结点时,同时用 map 存储新旧节点关系,以旧结点为 key,新节点为 value。
- 然后再根据 random 值作为 key 查找 map 从而得到新节点,并与之建立连接。
- 最后返回头结点即可。
复杂度分析:
- 时间复杂度涉及到 map 查找,所以本解法时间复杂度 >= O(n)
- O(n) 的额外空间复杂度。
代码
/*
// Definition for a Node.
class Node {
public int val;
public Node next;
public Node random;
public Node() {}
public Node(int _val,Node _next,Node _random) {
val = _val;
next = _next;
random = _random;
}
};
*/
class Solution {
Map<Node, Node> map = new HashMap<>();
public Node copyRandomList(Node head) {
Node dummy = new Node();
Node cursor = dummy;
Node p = head;
while(p != null) {
Node newNode = new Node(p.val, null, p.random);
map.put(p, newNode);
cursor.next = newNode;
cursor = cursor.next;
p = p.next;
}
cursor = dummy.next;
while(cursor != null) {
if(cursor.random != null) {
cursor.random = map.get(cursor.random);
}
cursor = cursor.next;
}
return dummy.next;
}
}
结果
解法二:使用 map 的递归解法
方法的宏观语义:获得一个以 head 为头结点的链表的深拷贝。(时刻牢记)
深拷贝一个结点等同于:
- 新建一个结点,并将与旧结点的映射关系放入 map 。
- 让新结点的 next 指向以旧结点的 next 为头节点的链表的深拷贝。
- 让新节点的 random 指向以旧结点的 random 为头节点的链表的深拷贝。
复杂度分析:
- 时间复杂度涉及到 map 查找,所以本解法时间复杂度 >= O(n)
- O(n) 的额外空间复杂度。
代码
/*
// Definition for a Node.
class Node {
public int val;
public Node next;
public Node random;
public Node() {}
public Node(int _val,Node _next,Node _random) {
val = _val;
next = _next;
random = _random;
}
};
*/
class Solution {
Map<Node, Node> map = new HashMap<>();
public Node copyRandomList(Node head) {
if(head == null) return null;
if(map.containsKey(head)) return map.get(head);
Node newNode = new Node(head.val, null, null);
map.put(head, newNode);
newNode.next = copyRandomList(head.next);
newNode.random = copyRandomList(head.random);
return newNode;
}
}
结果
解法三:连成一链,再拆分
是推荐的做法,因为不需要使用 map 等额外的数据结构。
参考《剑指Offer》
复杂度分析:
- O(n) 时间复杂度
- O(1) 的额外空间复杂度。
代码
/*
// Definition for a Node.
class Node {
public int val;
public Node next;
public Node random;
public Node() {}
public Node(int _val,Node _next,Node _random) {
val = _val;
next = _next;
random = _random;
}
};
*/
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
Node p = head;
while(p != null) {
p.next = new Node(p.val, p.next, null);
p = p.next.next;
}
p = head;
while(p != null) {
if(p.random != null) {
p.next.random = p.random.next;
}
p = p.next.next;
}
p = head;
Node newHead = p.next;
while(p.next != null) {
Node cur = p.next;
p.next = cur.next;
p = cur;
}
return newHead;
}
}