📑1. 题目
给你一个长度为 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
或指向链表中的节点。
🦨2. 解法1 - 暴力求解
🥦2.1 思路
题目给的要求是将原链表进行拷贝,拷贝链表简单,但是这个链表里面还有一个random随机指针,它指向哪,这我们是无法确定的。
我们这里可以用一个暴力的方法,就是用数组的思想,看random函数指向第几个节点,记录其走的节点,拷贝的链表再走对应步数,即得到random所指向的位置。
需要多次遍历链表,时间复杂度为O(n2)。
🥦2.2 代码实现
struct Node* BuyNode(int x)
{
struct Node* node = (struct Node*)malloc(sizeof(struct Node));
if(node == NULL)
{
perror("malloc fail");
exit(-1);
}
node->val = x;
node->next = NULL;
node->random = NULL;
return node;
}
struct Node* copyRandomList(struct Node* head) {
struct Node* guard = NULL;
struct Node* tail = NULL;
guard = tail = (struct Node*)malloc(sizeof(struct Node));
tail->next = NULL;
struct Node*cur = head;
while(cur)
{
struct Node*tmp = BuyNode(cur->val);
tmp->next = cur->next;
tail->next = tmp;
tail = tail->next;
cur = cur->next;
}
cur = head;
struct Node*cur2 = guard->next;
while(cur2)
{
struct Node*cur1 = guard->next;
int count = 0;
struct Node*check = head;
while(check!=cur->random)
{
count++;
check = check->next;
}
while(count)
{
cur1 = cur1->next;
count--;
}
cur2->random = cur1;
cur2 = cur2->next;
cur = cur->next;
}
struct Node* newhead = guard->next;
free(guard);
return newhead;
}
🦋3. 解法2 - 拆分节点
暴力求解虽然能解决问题,但是这个思路十分的挫。如果在比赛时,想不出来倒是可以破釜沉舟试一试,但是在平时训练过程中,还是需要更新我们的思维,多刷题,自己思维逻辑自然会有一定的提高。
ps:
自己做这题时,实不相瞒,看题目都看了蛮久,看完又发愣了,想了一会,没有什么好的思路,直接暴力求解,代码也写的蛮乱(解法1就是自己原本的解法)。
🥕3.1 思路
这题的问题就是关于随机指针的处理,因此尝试跟踪旧链表和新链表中对应的节点,以便在新链表中设置随机指针。
我们将旧链表拆分,原节点和原节点的后继节点之间插入新节点,这样就能通过原节点找到我们复制的节点。拷贝节点的random,就是原节点random->next。
这里题目还有一个要求就是原链表不能被破坏,那我们再将链接上去的节点解下来,尾插到新的链表,然后把原链表恢复。
这里其实相当于考了对链表的增、删、查、改,考察还是很全面的。
🥕3.2 代码实现
struct Node* copyRandomList(struct Node* head) {
//链接复制节点
struct Node* cur = head;
while(cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
struct Node* next = cur->next;
cur->next = copy;
copy->next = next;
cur = next;
}
//找random位置
cur = head;
while(cur)
{
struct Node* copy = cur->next;
if(cur->random == NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;
}
cur = cur->next->next;
}
//卸复制节点,恢复原链表
cur = head;
struct Node* copyHead = NULL;
struct Node* copyTail = NULL;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
//尾插
if(copyHead == NULL)
{
copyHead = copyTail = copy;
}
else
{
copyTail->next = copy;
copyTail = copyTail->next;
}
//恢复链表
cur->next = next;
cur = next;
}
return copyHead;
}