力扣刷题笔记丨算法数据结构--链表--复制带随机指针的链表

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;      // 返回新链表头节点
    }
};

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值