一、题目描述
JZ25 复杂链表的复制
题目大意:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头节点
注意审题:我们需要对链表进行深拷贝
二、算法1(哈希表)
解题思路
- 假如链表没有随机指针, 我们的拷贝方式很简单, 只要先创建一个哨兵节点, 然后遍历一下给定链表, 创建新节点, 并不断和上个节点连接
- 本题的难点就在于每个节点还有一个指向空或其它节点的指针, 一种比较直观的想法就是先把整条链表连起来, 然后再挨着改变指针, 因此我们可以用哈希表来存放指针的映射关系, 然后根据将随机指针指向原链表随机指针映射在新链表的位置
- 算法实现:首先创建一个哨兵节点, 遍历一次原链表, 先将除随机指针外的部分创建并连接, 同时用哈希表记录指针之间的映射, 最后遍历一次哈希表, 将随机指针指向对应的位置
代码实现(C++11)
class Solution { public: RandomListNode* Clone(RandomListNode* pHead) { if(!pHead) return pHead; // 为空则直接返回空 unordered_map<RandomListNode*, RandomListNode*> mp; // 创建哈希表 RandomListNode* dummy = new RandomListNode(0); // 哨兵节点 RandomListNode *pre = dummy, *cur = pHead; // 指向哨兵和链表头的指针 while(cur){ RandomListNode* clone = new RandomListNode(cur->label); // 拷贝节点 pre->next = clone; // 与上个结点连接 mp[cur] = clone; // 记录映射关系 pre = pre->next; // 指针移动 cur = cur->next; } for(auto& [key, value] : mp){ // 遍历哈希表 value->random = key->random == NULL ? NULL : mp[key->random]; } delete dummy; // 释放哨兵节点空间 return mp[pHead]; } };
时间复杂度:O(n), 遍历一次链表和哈希表的时间
空间复杂度:O(n), 哈希表使用的空间
三、算法2(链表拼接、拆分)
解题思路
- 此解法参考了大佬的做法, 主要思路是将原链表的结点对应的拷贝节点连在其后, 最后链表变成 原1 -> 拷1 -> 原2 -> 拷2 -> ... -> null 的形式
- 然后我们再逐步处理对应的随机指针, 使用双指针, 一个指针指向原链表的节点, 一个指向拷贝链表的节点, 那么就有 拷->random = 原->random->next (random不为空)
- 最后再用双指针将两条链表拆分即可, 此算法大大优化了空间复杂度, 十分优秀
代码实现(C++11)
class Solution { public: RandomListNode* Clone(RandomListNode* pHead) { if(!pHead) return pHead; // 为空则返回 RandomListNode* cur = pHead; while(cur){ RandomListNode* tmp = new RandomListNode(cur->label); // 拷贝节点 tmp->next = cur->next; cur->next = tmp; cur = tmp->next; } RandomListNode *old = pHead, *clone = pHead->next, *ret = pHead->next; while(old){ clone->random = old->random == NULL ? NULL : old->random->next; // 处理拷贝节点的随机指针 if(old->next) old = old->next->next; // 注意特判空指针 if(clone->next) clone = clone->next->next; } old = pHead, clone = pHead->next; while(old){ // 拆分链表 if(old->next) old->next = old->next->next; if(clone->next) clone->next = clone->next->next; old = old->next; clone = clone->next; } return ret; } };
时间复杂度:O(n), 遍历了三次链表
空间复杂度:O(1), 使用了常数个链表指针变量