leetcode138.随机链表的复制

题目链接

leetcode

题目描述

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 XY 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 xy ,同样有 x.random --> y
返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0n-1);如果不指向任何节点,则为 null
代码只接受原链表的头节点 head 作为传入参数。

C++ 代码

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;

// 链表结点的定义
struct Node {
int val;
Node* next;
Node* random;
Node(int x) : val(x), next(nullptr), random(nullptr) {}
};

// 辅助函数:创建随机链表
Node* createList(const vector<pair<int, int>>& values) {
if (values.empty()) {
return nullptr;
}

int n = values.size();
vector<Node*> nodes(n);

for (int i = 0; i < n; i++) {
nodes[i] = new Node(values[i].first);
}

for (int i = 0; i < n; i++) {
if (i+1 < n) {
nodes[i]->next = nodes[i+1];
}
if (values[i].second != -1) {
nodes[i]->random = nodes[values[i].second];
}
}

return nodes[0];
}

// 辅助函数:打印随机链表
void printList(Node* head) {
cout << "[";
Node* cur = head;

while (cur != nullptr) {
cout << "[" << cur->val << ",";
if (cur->random != nullptr) {
cout << cur->random->val;
} else {
cout << "null";
}
cout << "]";
if (cur->next != nullptr) {
cout << ",";
}
cur = cur->next;
}
cout << "]" << endl;
}

// 辅助函数:释放链表内存
void deleteList(Node* head) {
while (head != nullptr) {
Node* temp = head;
head = head->next;
delete temp;
}
}

/*
回溯 + 哈希表

哈希表优化:
使用一个哈希表来存储原始结点和其复制结点之间的映射关系。
在复制过程中,如果已经复制过某个结点,则直接从哈希表中获取其复制结点。
复制链表:
对于链表中的每个结点,先检查哈希表中是否已经存在该结点的复制结点。
如果不存在,则创建新的复制结点,并将其添加到哈希表中。
然后,复制该结点的 next 和 random 指针。
递归复制:
对于 next 和 random 指针指向的结点,递归调用 copyRandomList 函数进行复制。

时间复杂度:O(n)
其中 n 是链表的长度。对于每个结点,
至多访问其「后继结点」和「随机指针指向的结点」各一次,均摊每个点至多被访问两次。
空间复杂度:O(n)
哈希表的空间开销。
*/
class Solution_0 {
public:
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return nullptr;
}

if (!cachedNode.count(head)) {
Node* newHead = new Node(head->val);
cachedNode[head] = newHead;
newHead->next = copyRandomList(head->next);
newHead->random = copyRandomList(head->random);
}

return cachedNode[head];
}

private:
unordered_map<Node*, Node*> cachedNode;
};

/*
迭代 + 结点拆分
每个结点后面复制一个结点,然后再分离出来。

时间复杂度:O(n)
其中 n 是链表的长度,需要遍历该链表三次。
空间复杂度:O(1)
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return nullptr;
}

// 复制原链表中的各个结点,构建新的链表
for (Node* node = head; node != nullptr; node = node->next->next) {
Node* newNode = new Node(node->val);
newNode->next = node->next;
node->next = newNode;
}

// 修改新链表中各个结点的 random 指向
for (Node* node = head; node != nullptr; node = node->next->next) {
Node* newNode = node->next;
newNode->random = (node->random != nullptr) ? node->random->next : nullptr;
}

// 拆分出原链表、新链表
Node* newHead = head->next;
for (Node* node = head; node != nullptr; node = node->next) {
Node* newNode = node->next;
node->next = node->next->next;
newNode->next = (newNode->next != nullptr) ? newNode->next->next : nullptr;
}

return newHead;
}
};

// 辅助函数:打印二维数组
void printArray(const vector<pair<int, int>>& nums) {
cout << "[";
for (size_t i = 0; i < nums.size(); ++i) {
cout << "[" << nums[i].first << ",";
if (nums[i].second == -1) {
cout << "null";
} else {
cout << nums[i].second;
}
cout << "]";
if (i != nums.size() - 1) cout << ",";
}
cout << "]" << endl;
}

int main() {
Solution solution;
vector<vector<pair<int, int>>> head_cases = {
{{7,-1}, {13,0}, {11,1}, {10,2}, {1,0}},
{{1,1}, {2,1}},
{{3,-1}, {3,0}, {3,-1}}
};

for (size_t i = 0; i < head_cases.size(); i++) {
auto& nums = head_cases[i];
cout << "Input: head = ";
printArray(nums);

// 创建随机链表
Node* head = createList(nums);
cout << "Original List: ";
printList(head);
// 复制随机链表
Node* copy = solution.copyRandomList(head);
cout << "Copied List: ";
printList(copy);

// 释放内存
deleteList(copy);
deleteList(head);
}

return 0;
}

leetcode138.随机链表的复制
https://lcf163.github.io/2024/04/07/leetcode138:随机链表的复制/
作者
乘风的小站
发布于
2024年4月7日
许可协议