1.前言
今天我们来回顾之前一道链表的题目,最近学了map容器,老师讲用map关联拷贝与源结点会很好做,特地记录一下。
2.题目简介
题目链接:LINK
3.求解思路
首先我们来回顾一下之前是怎么做这道题的:
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* head) {
if(head==NULL)
{
return NULL;
}
struct Node* pcur = head;
//插入一些复制结点
while(pcur)
{
struct Node* pushback = (struct Node*)malloc(sizeof(struct Node));
pushback->val = pcur->val;
pushback->next = pcur->next;
pcur->next = pushback;
pcur = pcur->next->next;//调整pcur
}
//控制拷贝结点的random
pcur = head;
while(pcur)
{
//如果原链表的random指向空,那我们新链表也要指向空
if(pcur->random == NULL)
{
pcur->next->random = NULL;
pcur = pcur->next->next;
continue;
}
pcur->next->random = pcur->random->next;
pcur = pcur->next->next;
}
//把拷贝结点放下来
pcur = head;
struct Node* pcurx = head->next;
struct Node* returnpcurx = pcurx;
while(pcur)
{
pcur->next = pcur->next->next;
pcur = pcur->next;
//最后一个节点
if(pcurx->next == NULL)
{
pcurx->next = NULL;
break;
}
pcurx->next = pcurx->next->next;
pcurx = pcurx->next;
}
//返回
return returnpcurx;
}
之前的整体思路是这样的:我们依次遍历原链表,顺便拷贝出一个一样的结点紧随其后,使之与原链表成为一条链表。
之后,我们再把这个链表分开,分成原链表和拷贝链表。
我们发现之前的时候要拷贝一个链表,然后费力把这个拷贝链表和原链表融合在一起,然后到最后又拆开。
3.1之前做法虽然麻烦,但是有道理
为啥要复制的时候合成一个链表到最后又拆成为两个呢?这不是纯纯给自己找麻烦吗?是的,现在来看的确很麻烦,但是在当时所掌握的知识上,这种方法却也是无奈之举。
之所以这样合在一起又拆开这样做,是因为题目要求拷贝节点的random指向与原结点指向一致。但是两个一上来分开拷贝,我们虽然知道知道原结点指向的是哪里,但是拷贝结点random的指向我们就不明确了,因为两个random之间没有什么关联性。
为了解决这个问题,当时我们想到了一个方法——算相对位置。就是我们拷贝的时候直接让拷贝链表结点紧随源节点其后,这样我们就用合成一个链表的方式强行让两个链表之间的random结点建立了联系。
等到链接好了random,后面又为了满足题目中的要求,我们又不得不再把两个链表拆开而已。
3.2现在map就可以建立两个结点之间的关联
之前只能通过合成一个链表来实现两个结点之间的random之间的关联性,现在我们可以通过容器map来建立两个结点之间的关联性。
所以我们可以得出下面代码:
4.示例代码
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
//我们使用operator[]的方法来解决这个问题,即利用map
class Solution {
public:
Node* copyRandomList(Node* head) {
//建立一个map,方便后序对copy链表和origin链表进行关联
map<Node*, Node*> nodeMap;
Node* copyHead = nullptr, *copyTail = nullptr, *cur = head;
//1.我们先把copy链表整出来,并且顺便把两个链表关联起来
while(cur)
{
if(copyHead == nullptr) //如果是第一个结点,要进行特殊处理
{
copyHead = copyTail = new Node(cur->val);
}
else //如果不是第一个,则挨个往后链接就行
{
copyTail->next = new Node(cur->val);
copyTail = copyTail->next;
}
//关联两个链表
nodeMap[cur] = copyTail;//这句话的意思是,把cur作为key值插入到map中,并且其V值是copy的对应的结点。
//继续下一个结点
cur = cur->next;
}
//2.我们第二步来处理random问题
cur = head;
Node* copy = copyHead;
while(cur)
{
if(cur->random == nullptr)//如果cur的结点的random部分指向的是空,那么我们对应的copy结点的random部分也要指向空
{
copy->random = nullptr;
}
else//如果不是指向空
{
copy->random = nodeMap[cur->random];//这个地方返回的其实是cur的random对应的copy的结点。因为我们前面做过关联了。
}
cur = cur->next;
copy = copy->next;
}
return copyHead;
}
};