数据结构每日亿题(四)
创始人
2024-01-20 17:09:36
0

复制带随机指针的链表

原题传送门:力扣
题目:
在这里插入图片描述这题的大概意思就是:
有这样一个链表,他比普通的链表多一个成员变量:random指针,这个random指针指向的是这个链表中随机一个地方,这个地方是其它节点的位置或者是空指针。现在题目的要求是让你把这个链表复制出来。

一.大概思路

这题的难点主要就是这个随机指针因为这是没有规律的可能这个链表的第一个节点指向第二个节点,但是另一个节点可能指向的就是第三个节点。
虽然这题可以用暴力方法来求解:就是先遍历第一遍找到第一个节点指向的位置在复制时把这个位置写出来,然后在遍历第二遍,第三遍…。这方法可以是可以但是时间复杂度是O(N^2),太多了,可以用但是不推荐。

接下来我来讲一个特别神奇的思路:

第一步

在这里插入图片描述先随便拿个链表做说明,我们在每个节点的后面添加一个新的节点,这个新的节点的里的值和它上个节点的值是一样的:
在这里插入图片描述像这样都连起来了,但是新节点的random还没有设置,接下来就是这个思路的厉害之处。

第二步

在这里插入图片描述先定义两个指针cur,next,我们虽然不知道新节点的random是指向哪里,但是我们直到原来节点的random指向哪里。我们先看第一个原节点的random指向的是NULL,现在我们把这种情况当成一种特殊情况,就是先判断原节点指向的是不是空,是的话新节点的random也指向空。

然后在判断其它情况,这里让cur,next设置好一个新节点的random后往后跳一步:
在这里插入图片描述我们注意看原节点13的random指向的应该是7这个节点,所以说现在我们希望next指向的这个新节点的random也指向新节点的7,但是我们有没有发现我们希望找到的位置就在原来7这个节点的后一步。
用代码来说:

cur->random->next

上面这个位置是不是就是我们新节点13的random指向的位置?
在这里插入图片描述像这样next,cur一步一步的往后走,是不是就把新加上去的这些节点的random都配置好了。
这种方法的好处就是不管你之前节点的random指向哪个地方,我新节点都指向你这个地方的下一个位置

第三步

新节点的random都配置好了,但是我们返回的是一个新链表,所以现在我们要把之前新加的那些节点都取出来合成一个新的链表。取下来也不难,就是将一个指针从头开始遍历。

二.代码实现

第一步

	//在每个节点后面插一个复制的节点struct Node* cur = head;while(cur){struct Node* next = cur->next;struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));newnode->val = cur->val;cur->next = newnode;newnode->next = next;cur = next;}

接下来我会将代码分成几部分来讲:

    struct Node* cur = head;while(cur){struct Node* next = cur->next;struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));

先把前戏做好,定义好三个指针:
在这里插入图片描述
这个代码时把新节点的值改成原来一样的值。

newnode->val = cur->val;

然后把三个节点链接起来就行。

cur->next = newnode;
newnode->next = next;

在这里插入图片描述

最后cur指向下一个节点继续往后走,这样就把所有的新节点和原来的节点放好了。

cur = next;

第二步

    //设置复制的节点的rondomcur = head;while(cur){   struct Node* next = cur->next;if(cur->random == NULL){next->random = NULL;}else{next->random = cur->random->next;}cur = next->next;}

我们先将cur指针恢复到原位:

cur = head;

然后在看循环里面的内容:

这是一个特殊的判断,如果原节点的random指向空,我们这个新节点的random也指向空。

struct Node* next = cur->next;
if(cur->random == NULL)
{next->random = NULL;
}

再看普通情况:

else
{next->random = cur->random->next;
}

把新节点的random指向原来节点的random的下一个节点。

最后cur指向下一个位置继续判断

cur = next->next;

在这里插入图片描述注意,这个下一个位置是原来链表那些节点的第二个位置:
在这里插入图片描述因为next每次在循环的开始就已经指向了当前cur的next,所以不用在对其进行相关的操作了。

第三步

    //将复制在上面的节点取下来尾插变成新的链表cur = head;struct Node* nhead = NULL;struct Node* tail = NULL;while(cur){struct Node* next = cur->next;struct Node* curnext = next->next;if(nhead == NULL){nhead = tail = next;}else{tail->next = next;tail = tail->next;}cur->next = curnext;cur = curnext;} 

最后我们就要进行收尾操作:尾插。

先定义两个指针nhead,tail然后再把cur指针复位重新指向链表最开始的位置:

cur = head;
struct Node* nhead = NULL;
struct Node* tail = NULL;

在这里插入图片描述

然后在定义两个指针:

struct Node* next = cur->next;
struct Node* curnext = next->next;

在这里插入图片描述

尾插同样要分两种情况,第一种情况就是tail,nhead为空时,也就是刚开始的时候。

if(nhead == NULL)
{nhead = tail = next;
}

这时候我们让nhead,tail都指向此时next指向的节点:
在这里插入图片描述这里7这个节点原来指向啥还是啥,我们现在看的像是把这个节点拿了下来。

拿下来一个节点之后,我们最后把原来两个节点还原:

cur->next = curnext;

在这里插入图片描述

然后cur继续往后跳两步:

cur = curnext;

在这里插入图片描述curnext和next每次在循环之前就已经指向了该指向的位置。

接下来我们再来看当nhead,tail不是空的情况:
在这里插入图片描述现在我们需要将next指向的节点取下来:

else
{tail->next = next;tail = tail->next;
}

在这里插入图片描述这样就把13这个节点取下来了,接下来的操作就是像这样一直循环一直循环,直到cur指针直到空时结束。现在就把最开始建立的那些新节点全部拿下来并且链接在一起了。
现在不用担心random的问题,因为第三步,根本没改变过每个节点里random的内容,所以之前指向哪里,现在还指向哪里。

最后结果:
在这里插入图片描述每个节点random指向的节点我就不画了。现在我们返回nhead就行了。

三.代码

/*** Definition for a Node.* struct Node {*     int val;*     struct Node *next;*     struct Node *random;* };*/struct Node* copyRandomList(struct Node* head) {//在每个节点后面插一个复制的节点struct Node* cur = head;while(cur){struct Node* next = cur->next;struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));newnode->val = cur->val;cur->next = newnode;newnode->next = next;cur = next;}//设置复制的节点的rondomcur = head;while(cur){   struct Node* next = cur->next;if(cur->random == NULL){next->random = NULL;}else{next->random = cur->random->next;}cur = next->next;}//将复制在上面的节点取下来尾插变成新的链表cur = head;struct Node* nhead = NULL;struct Node* tail = NULL;while(cur){struct Node* next = cur->next;struct Node* curnext = next->next;if(nhead == NULL){nhead = tail = next;}else{tail->next = next;tail = tail->next;}cur->next = curnext;cur = curnext;}    return nhead;
}

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...