题目描述
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
- val:一个表示 Node.val 的整数。
- random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
解法1:使用一个HashMap
新建一个key和v都是Node的HashMap,先遍历一次原链表,遍历的过程将原来的节点作为key,根据val新建一个Node作为v。第一次遍历完成后,依次生成了next和ran都为null的只有值的节点。
再次从头遍历原链表,get到原链表节点对应的v,将这个v的next指针和rand指针都指到对应的位置,最后返回map.get(head)
需要遍历两次链表,时间复杂度为O(n),空间复杂度为O(n)
代码:
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
Map<Node, Node> map = new HashMap<Node, Node> ();
Node temp = head;
while (temp != null){
map.put(temp, new Node(temp.val));
temp = temp.next;
}
temp = head;
while (temp != null) {
map.get(temp).next = map.get(temp.next);
map.get(temp).random = map.get(temp.random);
temp = temp.next;
}
return map.get(head);
}
}
解法2:不需要哈希表,只用有限的几个变量完成
总结来说:
(1)在后面新建节点
(2)新建节点的rand指到原来的rand的下一个:本题的痛点就在于不容易找到rand,本解法巧妙地解决了这个问题
(3)分离
时:O(n)
空:O(1)
代码:
public Node copyRandomList1(Node head){
if (head == null){
return null;
}
Node temp = head;
//在后面新建
while (temp != null) {
Node tempNext = temp.next;
temp.next = new Node(temp.val);
temp.next.next = tempNext;
temp = temp.next.next;
}
temp = head;
//复制rand指针
while (temp != null) {
Node curr = temp.next;
curr.random = temp.random != null ? temp.random.next : null;
temp = temp.next.next;
}
Node dummy = new Node(-1);
Node newTemp = dummy;
temp = head;
//拆分
while (temp != null){
newTemp.next = temp.next;
newTemp = newTemp.next;
temp.next = temp.next.next;
temp = temp.next;
}
newTemp.next = null;
return dummy.next;
}