请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
方法一:哈希表
分析:复制一个链表是容易的,复杂链表的难点在于多了一个random指针,指向链表中的任意节点或者null 。这个 random 指针意味着在复制过程中,除了构建前驱节点和当前节点的引用指向 pre.next ,还要构建前驱节点和其随机节点的引用指向 pre.random
(1):我们可以使用哈希表的映射关系,先构建
unordered_map<旧链表 ,新链表> mp,构造出新的链表,并使用默认构造,添加其val值,遍历即可获得链表的所有节点 val
(2):理解mp[旧节点] = 新节点 ;mp[旧节点->next] = 后向指针位置 的基础上,为节点添加random和next指针,
mp[cur]->next = mp[cur->next];
(新节点)->next = 后向指针位置
mp[cur]->random = mp[cur->random]。。。。
/*
// Definition for a Node.
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) {
Node * cur = head;
//判断原始链表是否为空
if(cur==nullptr)
return NULL;
//使用哈希表进行放置旧链表,和新复制的链表
unordered_map<Node* ,Node*> mp;
while(cur!=nullptr){
mp[cur] = new Node(cur->val);
cur = cur->next;
}
//现在 cur已经指向链表尾部 重新移回
cur = head;
//给新链表添加next,randow节点
while(cur!=nullptr){
mp[cur]->next = mp[cur->next];
mp[cur]->random = mp[cur->random];
cur = cur->next;
}
//现在 cur已经指向链表尾部 重新移回
cur = head;
return mp[cur];
//return mp[head];
}
};
- 时间复杂度 O(N): 两轮遍历链表,使用O(N) 时间。
- 空间复杂度 O(N) : 哈希表使用线性大小的额外空间。
方法二:拼接+拆分
考虑构建 原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> …… 的拼接链表,如此便可在访问原节点的 random 指向节点的同时找到新对应新节点的 random 指向节点
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
// 1. 复制各节点,并构建拼接链表
while(cur != nullptr) {
Node* tmp = new Node(cur->val);
tmp->next = cur->next;
cur->next = tmp;
cur = tmp->next;
}
// 2. 构建各新节点的 random 指向
cur = head;
while(cur != nullptr) {
if(cur->random != nullptr)
cur->next->random = cur->random->next;
cur = cur->next->next;
}
// 3. 拆分两链表
cur = head->next;
Node* pre = head, *res = head->next;
while(cur->next != nullptr) {
pre->next = pre->next->next;
cur->next = cur->next->next;
pre = pre->next;
cur = cur->next;
}
pre->next = nullptr; // 单独处理原链表尾节点
return res; // 返回新链表头节点
}
};
- 时间复杂度 O(N) : 三轮遍历链表,使用 O(N) 时间。
- 空间复杂度 O(1) : 节点引用变量使用常数大小的额外空间。