LeetCode 面试经典 150_链表_随机链表的复制(59_138_C++_中等)
LeetCode 面试经典 150_链表_随机链表的复制(59_138_C++_中等)
- 题目描述:
- 输入输出样例:
- 题解:
- 解题思路:
- 思路一(哈希表):
- 思路二(迭代 + 节点拆分):
- 代码实现
- 代码实现(思路一(哈希表)):
- 代码实现(思路二(迭代 + 节点拆分)):
- 以思路二为例完成代码调试
题目描述:
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。
输入输出样例:
示例 1:

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

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
提示:
0 <= n <= 1000
-104 <= Node.val <= 104
Node.random 为 null 或指向链表中的节点。
题解:
解题思路:
思路一(哈希表):
1、我们可以直接将原链表和复制链表中对应的结点存储在哈希表中,这样我们就可以直接通过原地址的 next 和 random 来更新复制链表的指向。
例:

2、具体思路如下:
① 创建一个哈希表用于存储两个链表中每个结点地址的对应关系。
② 遍历原链表的同时创建复制链表的 val ,并使用哈希表记录原链表结点地址和复制链表地址的对应关系。
③ 用过哈希表更新复制链表的 next 和 random指向。
3、复杂度分析
① 时间复杂度:O(N),N代表原链表所含结点的数量。创建复制结点的时间复杂度为N,更新结点的指向的时间消耗为N,所以时间复杂度为O(N)。
② 空间复杂度:O(N),,使用了一个额外的哈希表来存储结点地址的对应关系(除存储答案所需空间)。
思路二(迭代 + 节点拆分):
1、我们可以将复制结点创建出来之后直接插入在原结点后边。这样我们在更新random的指向时就可以直接通过原结点random的指向来更新复制结点random的指向。
例:

2、具体思路如下:
① 在每个原结点后边创建一个复制结点并插入,将复制结点的random设置为nullptr。
② 遍历创建后的链表,更新复制结点的random的指向。
③ 将复制结点查下来,将原链表进行复原。
3、复杂度分析
① 时间复杂度:O(N),N代表原链表所含结点的数量。创建复制结点的时间复杂度为N,更新结点的指向的时间消耗为N,所以时间复杂度为O(N)。
② 空间复杂度:O(1),除存储答案所需空间。
代码实现
代码实现(思路一(哈希表)):
class Solution1 {
public:// 复制带有随机指针的链表Node* copyRandomList(Node* head) {// 创建一个unordered_map来存储原链表节点和新链表节点的映射关系unordered_map<Node*, Node*> map;// 第一步:遍历原链表,为每个节点创建一个新的节点Node *curNode = head;while (curNode != nullptr) {// 为当前节点创建一个新的节点,并将原节点和新节点的映射添加到map中Node *newNode = new Node(curNode->val);map[curNode] = newNode;// 移动到下一个节点curNode = curNode->next;}// 第二步:重新遍历原链表,设置新节点的 next 和 random 指针curNode = head;while (curNode != nullptr) {// 为新节点设置 next 和 random 指针,map[curNode->next] 和 map[curNode->random] 通过原节点找到对应的新节点map[curNode]->next = map[curNode->next]; // 设置新节点的 next 指针map[curNode]->random = map[curNode->random]; // 设置新节点的 random 指针// 移动到下一个节点curNode = curNode->next;}// 返回新链表的头节点return map[head];}
};
代码实现(思路二(迭代 + 节点拆分)):
class Solution2 {
public:Node* copyRandomList(Node* head) {if (head == nullptr) {return nullptr;}Node* head1 = head;// 创建复制的结点插入原链表while (head1 != nullptr) {Node* newNode = new Node(head1->val);newNode->next = head1->next;head1->next = newNode;head1 = head1->next->next;}//更新head1指向首节点head1 = head;// 修改新创建结点random的指向while (head1 != nullptr) {if (head1->random != nullptr) {head1->next->random = head1->random->next;}head1 = head1->next->next;}//将链表一和链表二进行拆分,不使用新的头节点 head1 = head;Node* head2 = head->next;Node* tmp_head2 = head->next;while (head2->next != nullptr) {head1->next = head1->next->next;head2->next = head1->next->next;head1 = head1->next;head2 = head2->next;}head1->next = nullptr;return tmp_head2;//将链表一和链表二进行拆分//使用一个新的头节点来链接拆分的链表二 // Node *dummyNode=new Node(0);// head1=head;// Node *head2=dummyNode;// while(head1!=nullptr){// head2->next=head1->next;// head2=head1->next;// head1->next=head2->next;// head1=head2->next;// }// head2=dummyNode->next;// delete dummyNode;// return head2;}
};
以思路二为例完成代码调试
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;class Node{
public:int val;Node *next;Node *random;Node(int _val){val=_val;next=NULL;random=NULL;}
};/*创建随机链表*/
Node *createNode(vector<vector<int>> &arr){Node *head=nullptr,*tail=nullptr;int n=0;//id_adress保存每个结点的序号和id,使其对应 unordered_map<int,Node *> id_adress;//采用尾插法先创建一个单链表,先使得random=nullptr for(const auto &i:arr){if(head==nullptr){head=tail=new Node(i[0]);}else{tail->next=new Node(i[0]);tail=tail->next;}id_adress[n]=tail;++n;}//将random通过adress_id进行赋值,-1代表nullptr tail=head;for(const auto &i:arr){if(i[1]==-1){tail=tail->next;continue;}tail->random=id_adress[i[1]];tail=tail->next;}return head;
}class Solution1 {
public:// 复制带有随机指针的链表Node* copyRandomList(Node* head) {// 创建一个unordered_map来存储原链表节点和新链表节点的映射关系unordered_map<Node*, Node*> map;// 第一步:遍历原链表,为每个节点创建一个新的节点Node *curNode = head;while (curNode != nullptr) {// 为当前节点创建一个新的节点,并将原节点和新节点的映射添加到map中Node *newNode = new Node(curNode->val);map[curNode] = newNode;// 移动到下一个节点curNode = curNode->next;}// 第二步:重新遍历原链表,设置新节点的 next 和 random 指针curNode = head;while (curNode != nullptr) {// 为新节点设置 next 和 random 指针,map[curNode->next] 和 map[curNode->random] 通过原节点找到对应的新节点map[curNode]->next = map[curNode->next]; // 设置新节点的 next 指针map[curNode]->random = map[curNode->random]; // 设置新节点的 random 指针// 移动到下一个节点curNode = curNode->next;}// 返回新链表的头节点return map[head];}
};int main(){vector<vector<int>> a={{7,-1},{13,0},{11,4},{10,2},{1,0}};Solution1 s;//创建原随机链表Node *head =createNode(a);//随机链表的复制Node *test =s.copyRandomList1(head);//只输出复制随机链表的random指向while(test!=nullptr){if(test->random!=nullptr){cout<<test->random->val<<" ";}else{cout<<"nullptr ";}test=test->next;}cout<<"null"; return 0;
}
LeetCode 面试经典 150_链表_随机链表的复制(59_138)原题链接
欢迎大家和我沟通交流(✿◠‿◠)
