idea: 刚开始没有思路,被题目弄懵了,我能想到的方法就是先复制不带random指针的链表,之后由表头到表尾再将每个结点的random指针通过循环进行连接,时间复杂度肯定时很高的,具体代码也没有
实现。之后看到题解,经过就牛二虎之力也是堪堪看懂。
idea 之题解: 见注释,也是看了好半天才把注释写出来。整体思路是利用递归的思想解决如何复制带有两个指针的结点,将当前结点两个指针指向结点的复制工作分开来完成,写成两条语句,进而解决了问题。期间,利用哈希表快速找到原random指针指向结点的映射。
法一: 递归+哈希表
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
unordered_map<Node*, Node*> newList; //建立哈希表用于储存新建结点,防止重复生成同一结点
Node* copyRandomList(Node* head) {
if(head == nullptr){ //判断传入结点是否为空
return nullptr;
}
if( !newList.count(head) ){ //检查结点是否已存在,或者已经被建立过
Node* headNew = new Node(head->val); //给新建结点赋值
newList[head] = headNew; //newhead为原表head结点的映射,或者说新建表中newhead为原表head的实时替身
headNew->next = copyRandomList(head->next); //递归依次生成新表的各个next结点,直到表尾
//该条语句作用原理:新表与原表同步位移,通过生成对应next结点的替身接到新表的结点完成任务
headNew->random = copyRandomList(head->random); //在上一条语句回溯过程中,该条语句开始产生作用,上一条语句每一次回溯,该条语句开始为当前结点生成其random指针指向的结点,由于经由上一条语句递归作用后新表每一个结点的next指针指向的结点都已设置好,所以该语句直接返回原表相同位置结点的替身结点,不发生递归
}
return newList[head]; //对应替身结点已存在,则直接返回
}
};
复杂度分析
时间复杂度:O(n)O(n),其中 nn 是链表的长度。对于每个节点,我们至多访问其「后继节点」和「随机指针指向的节点」各一次,均摊每个点至多被访问两次。
空间复杂度:O(n)O(n),其中 nn 是链表的长度。为哈希表的空间开销。
法二:迭代+结点拆分 idea:方法太巧妙了。直接复制结点,并直接连接到原表中,形成A-A`-B-B`-C-C`的情况,此时A`的random指向的结点为A->random->next,巧妙的解决了random指针的问题
![]()
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==NULL){ //检查是否为空链表
return NULL;
}
for(Node*p=head; p!=NULL; p=p->next->next){ //循环复制每一个结点,并插入到其后
Node*newnode=new Node(p->val); //复制结点
newnode->next=p->next; //插入操作
p->next=newnode;
}
for(Node*p=head; p!=NULL; p=p->next->next){ //循环为替身节点配置random指针,为原random的后继结点
p->next->random = p->random == NULL ? NULL : p->random->next;
}
Node* newhead = head->next; //保存新表头结点
for(Node* p=head; p!=NULL; p=p->next){ //将新表从原表中脱离出来
Node* newnode=p->next;
p->next = p->next->next;
newnode->next = newnode->next == NULL ? NULL :newnode->next->next; //到表尾时后附null,防止出现null->next情况
}
return newhead;
}
};
复杂度分析
时间复杂度:O(n)O(n),其中 nn 是链表的长度。我们只需要遍历该链表三次。
读者们也可以自行尝试在计算拷贝节点的随机指针的同时计算其后继指针,这样只需要遍历两次。
空间复杂度:O(1)O(1)。注意返回值不计入空间复杂度。
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/copy-list-with-random-pointer/solution/fu-zhi-dai-sui-ji-zhi-zhen-de-lian-biao-rblsf/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。