剑指 Offer 35. 复杂链表的复制

138. 复制带随机指针的链表

题目描述

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

输入输出

1
2
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

基本思路

哈希解法:按照往常的链表复制,可通过构造一个前驱结点依次复制,但是本题带random指向无法确定前驱结点的random指向哪里。利用哈希表将每一个结点依次复制进入哈希表中Map<Node, Node> map = new HashMap<>();然后构建一个新链表的引用指向即可。

时间复杂度:O(N) 两轮遍历链表

空间复杂度:O(N) 使用哈希表的额外空间


拼接拆分解法:

  1. 将原来的链表重组为:Node1 -> Node1New -> Node2 -> Node2New …
  2. 重新构建新链表的random指向
  3. 拆分两个链表,拆掉原来的留下新构造的并返回

时间复杂度:O(N) 三轮遍历链表

空间复杂度:O(1) 节点引用变量使用常数大小的额外空间

(Tips:如果把结果本身算上是O(N) 如果把结果视为必须使用的空间而排除在外那么就是 O(1) “原本就要新建一个链表”)

java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/*
// 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) {
if(head == null){
return null;
}
Node cur = head;
Map<Node, Node> map = new HashMap<>();
while(cur != null){
map.put(cur, new Node(cur.val));
cur = cur.next;
}

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);
}
}

// 拼接拆分解法
class Solution {
public Node copyRandomList(Node head) {
if(head == null){
return null;
}
Node cur = head;
// 1.复制节点拼接链表
while(cur != null){
Node tmp = new Node(cur.val);
tmp.next = cur.next;
cur.next = tmp;
cur = tmp.next;
}

// 2.构建random指向
cur = head;
while(cur != null){
if(cur.random != null){
cur.next.random = cur.random.next;
}
cur = cur.next.next;
}

// 3.拆分原来的链表
cur = head.next;
Node pre = head, res = head.next;
while(cur.next != null){
pre.next = pre.next.next;
cur.next = cur.next.next;
pre = pre.next;
cur = cur.next;
}
pre.next = null;
return res;
}
}