class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
class Solution {
public:
Node* copyRandomList(Node* head) {
}
};
初始化一个map,第一次遍历原始链表,使用这些链表的值拷贝,并且map[旧节点] = 新节点
第二次遍历链表,给新节点之间连上线
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == NULL){
return NULL;
}
Node *origin = head;
std::unordered_map<Node *, Node *> map;
while (origin){
Node *clone = new Node(origin->val);
map[origin] = clone;
origin = origin->next;
}
origin = head;
while (origin){ // 当前节点不为空
if(origin->next){ // 当前节点的下一个节点不为空
map[origin]->next = map[origin->next];
}
if(origin->random){
map[origin]->random = map[origin->random];
}
origin = origin->next;
}
return map[head];
}
};
回溯算法的第一想法是将链表想象成一张图。链表中每个节点都有2个指针(图中的边)。因为随机指针给图结构添加了随机性,所以我们可能会访问相同的节点多次,这样就形成了环
上图中,我们可以看到随机指针指向了前一个节点,因此形成了环。我们需要考虑这种环的实现。
此方法中,我们只需要遍历整个图并且拷贝它。拷贝的意思是每当遇到一个新的没有访问过的节点,你都要创造一个新的节点。遍历按照深度优先进行。我们需要在回溯的过程中记录已经访问过的节点,否则因为随机指针的存在进入死循环。
算法:
当我们遍历到某个点时,如果我们已经有了当前节点的一个拷贝,我们不需要重复进行拷贝。
如果我们还没拷贝过当前节点,我们创造一个新的节点,并把该节点放到已访问字典中
我们针对两种情况进行回溯调用:一个顺着 random 指针调用,另一个沿着 next 指针调用。步骤 1 中将 random 和 next 指针分别红红色和蓝色标注。然后我们分别对两个指针进行函数递归调用:
class Solution {
std::unordered_map<Node *, Node *> map;
public:
Node* copyRandomList(Node* head) {
if (head == nullptr){
return nullptr;
}
if(map.count(head)){
return map[head];
}
Node *node = new Node(head->val);
node->next = nullptr;
node->random = nullptr;
map[head] = node;
node->next = copyRandomList(head->next);
node->random = copyRandomList(head->random);
return node;
}
};
时间复杂度:O(N) ,其中 NN 是链表中节点的数目。
空间复杂度:O(N)。如果我们仔细分析,我们需要维护一个回溯的栈,同时也需要记录已经被深拷贝过的节点,也就是维护一个已访问字典。渐进时间复杂度为 O(N)。
class Solution {
std::unordered_map<Node *, Node *> map;
Node *getCloneNode(Node *node){
if(node == nullptr){
return nullptr;
}
if(map.count(node)){
return map[node];
}else{
Node *newNode = new Node(node->val);
map.insert(node, newNode);
return map[node];
}
}
public:
Node* copyRandomList(Node* head) {
if (head == nullptr){
return nullptr;
}
Node *oldNode = head;
Node *newNode = new Node(head->val);
map.insert(head, newNode);
while (oldNode != nullptr){
newNode->next = getCloneNode(oldNode->next);
newNode->random = getCloneNode(oldNode->random);
newNode = newNode->next ;
oldNode = oldNode->next;
}
return map[head];
}
};
时间复杂度:O(N)。因为我们需要将原链表逐一遍历。
空间复杂度:O(N)。 我们需要维护一个字典,保存旧的节点和新的节点的对应。因此总共需要 N 个节点,需要 O(N)的空间复杂度。
cloned_node.next = original.next
original.next = cloned_node
class Solution {
public:
Node* copyRandomList(Node* head) {
if (head == nullptr){
return nullptr;
}
Node *origial = head;
while (origial != nullptr){
Node *node = new Node(origial->val);
node->next = origial->next;
origial->next = node;
origial = origial->next->next;
}
// 跳出循环之后:origial = null
origial = head;
while (origial != nullptr){
if (origial->random != nullptr){
origial->next->random = origial->random->next;
}
origial = origial->next->next;
}
//拆分
origial = head;
Node *clone = head->next;
Node *cur_clone = clone;
while (origial!= nullptr && cur_clone != nullptr){
// 原始链表
origial->next = origial->next == clone ? clone : origial->next->next;
origial = origial->next;
cur_clone->next = cur_clone->next == clone ? clone : cur_clone->next->next;
cur_clone = cur_clone->next;
}
return clone;
}
};
题目 | 思路 |
---|---|
leetcode:133. 克隆图 Clone Graph | |
leetcode:138. 复制带随机指针的链表 Copy List with Random Pointer |