这一题目是非常经典的,可以完美检验链表知识点的掌握情况
题目链接
题目
给你一个长度为
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
作为传入参数。
解题思路和图解
我们看见这个题目的时候首先就是看不懂,有点懵的。其实这都是我们想复杂了、这里你不能来回去追求random指向的节点的哪里,只需要直接进行拷贝就可以
步骤1(考察链表的创建,赋值):
- 首先我们需要复制链表节点
- 然后我们把赋值的节点里面是数值val放到复制的节点里面,这里是不需要赋值next的,因为你只需要直接进行插入就可以,形成新的长的链表。
- 一边复制一边把节点插入到原链表里面形成一个
步骤2(考察链表的逻辑关系):
- 此时我们复制val到链表里面并没有复制random也就是随机节点,所以这里我们需要复制随机节点
- 首先我们判断这个节点里面的random指向的节点是不是NULL,如果是NULL也就是说明指向空,也就是最后一个节点
- 如果不是NULL ,也就是说明,这个节点指向的下一个节点不是NUILL,此时我们需要注意,这里的关键点来了。我们需要让拷贝节点里面的random的地址是原链表的random指向的下一个节点,也就是random的->next,这样我们就达到我们不需要进行指向,直接就把地址拷贝过来。
- 意思就是,拷贝链表节点,直接存放下一个节点的random
步骤3(拷贝链表的插入):
- 最后一步,很简单,只需要把链表拷贝的节点和原链表分开
- 最后返回新链表的头结点
题目代码
/** * Definition for a Node. * struct Node { * int val; * struct Node *next; * struct Node *random; * }; */ //这里进行重命名一下,后期使用的时候不需要一直写struct typedef struct Node Node; Node* copyRandomList(Node* head) { //创建节点,复制节点,插入节点 Node* cur = head; while(cur) { //创建节点,并且进行检验 Node* copy = (Node*)malloc(sizeof(Node)); if(copy == NULL) { perror("copyRandomList:copy:error"); exit(1); } //复制数值 copy->val = cur->val; //插入链表(这一步,两个不能交换) copy->next = cur->next; cur->next = copy; //遍历 cur = copy->next; } //复制random节点 cur = head;//cur一移动了重新指向头结点 while(cur) { Node* copy = cur->next;//找到copy拷贝节点,这里必须是循环里面的,因为每次都需要改变位置 if(cur->random == NULL) { copy->random = NULL; } else { copy->random = cur->random->next; } //遍历 cur = copy->next; } //取出需要的节点,返回头结点 cur = head;//cur一移动了重新指向头结点 Node* copyhead = NULL;Node* copytile = NULL;//创建头结点和尾结点,不创建哨兵位了 while(cur) { //这一步已经完成循环移动了,我们只需要每次移动cur就可以完成整体的移动 Node* copy = cur->next; Node* pure = copy->next; if(copytile == NULL) { copyhead = copytile = copy; } else { //进行尾插 copytile->next = copy; copytile = copytile->next; } cur = pure; } //返回新链表头结点 return copyhead; }