1 题目分析
链接:138. 复制带随机指针的链表 - 力扣(LeetCode) (leetcode-cn.com)
给你一个长度为 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 作为传入参数
该链表的结构如下:除了Next指针外,还存在一个random指针
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
思路一:哈希表(不太会做)
思路二:组合+拆分链表
本题关键在于如何在复制时储存random指针,由于random指针中含有之后的节点,因此必须存在一个原链表的完全复制,让新链表在构建时能够有指向。第一种使用键值储存原链表节点数据位置,第二种使用简单的结构储存节点数据,让新链表构建时指向方法较为方便。
2 哈希表相关知识
参考资料:数据结构 Hash表(哈希表)_积跬步 至千里-CSDN博客_哈希表
C++ 中使用哈希表(unordered_map)的常用操作_hero_th的博客-CSDN博客_c++哈希表操作
2.1 哈希函数
数值与数值地址的映射函数
index=H(key)
2.2 哈希函数的构造
- 直接定制
- 数字分析
- ……
不能太复杂,分布均匀
哈希冲突及解决方法
// 建立哈希表<a,b>从b类型到a类型的映射
unordered_map<int,int> map; //<string,string>,<char,char>
2.3 哈希表的操作
对于给定的key,计算hash地址index = H(key)
添加元素
map.insert(pair<int,int>(1, 10));
map.insert(pair<int,int>(2, 20));
map[3]=30;
map[4]=40;
查找
m.end() //指向哈希表的最后一个容器,实则超出了哈希表的范围,为空
m.find(2) //查找key为2的键值对是否存在 ,若没找到则返回m.end()
m.count(3) //查找哈希表中key为3的键值对,返回其数量,为1,则找到,若没找到则返回0
遍历
unordered_map<int, int> count;
for (auto p : count) {
int front = p.first; //key
int end = p.second; //value
}
3 代码实现
3.1 哈希表方法
- 初始化哈希列表与当前节点cur
- 遍历链表,建立哈希表的键值对
- 第二次遍历链表,复制next,random指针
- 最后返回带两个指针的哈希表
class Solution {
public:
Node* copyRandomList(Node* head) {
// 非空判断
if (head == nullptr) return nullptr;
// 初始化哈希表与节点
Node* cur = head;
unordered_map<Node*,Node*> map;
// 第一次遍历,建立键值对
while (cur != nullptr){
map[cur] = new Node(cur->val);
cur = cur->next;
}
// 第二次遍历,建立next与random指针
cur = head;
while(cur != nullptr){
map[cur]->next = map[cur->next];
map[cur]->random = map[cur->random];
cur = cur->next;
}
return map[head];
}
};
3.2 组合+拆分链表
- 第一次遍历,在原链表之后,复制新链表节点
- 第二次遍历,指定新链表节点的random指针
- 第三次遍历,通过拆分,指定新链表节点的next指针
这段自己写的代码超时了,下面贴的代码来自:图解算法数据结构 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台 (leetcode-cn.com)
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
// 1. 复制各节点,并构建拼接链表
while(cur != nullptr) {
Node* tmp = new Node(cur->val);
tmp->next = cur->next;
cur->next = tmp;
cur = tmp->next;
}
// 2. 构建各新节点的 random 指向
cur = head;
while(cur != nullptr) {
if(cur->random != nullptr)
cur->next->random = cur->random->next;
cur = cur->next->next;
}
// 3. 拆分两链表
cur = head->next;
Node* pre = head, *res = head->next;
while(cur->next != nullptr) {
pre->next = pre->next->next;
cur->next = cur->next->next;
pre = pre->next;
cur = cur->next;
}
pre->next = nullptr; // 单独处理原链表尾节点
return res; // 返回新链表头节点
}
};