链表-力扣hot100-随机链表的复制138

用很白话的话说,这题就是:

有一条「很特别的链表」,每个节点不止有 next(下一个),还有一个 random(随便指向谁)。

你要再造一条一模一样的新链表,但:

  • 新链表里的每个节点,都是新的对象(不能直接用原来的节点)

  • 新链表的结构(next 链接)要和原来一模一样

  • 新链表里的 random 关系也要和原来一模一样

    比如:原来 A.random 指向 B,那新链表里的 a.random 就要指向 b

关键点有三个:

  1. 每个节点有两个指针

    • next:正常链表的下一个

    • random:可以指向

      • 链表里的任意一个节点

      • 或者是 null

  2. 要做的是"深拷贝"

    • 不能只复制数值和指针引用到原节点,那叫"浅拷贝"

    • 要重新 new 出 n 个新节点

    • 新节点的 val 跟原节点一样

    • 新节点的 next 和 random 也串成同样的结构,但只能指向新链表中的节点

  3. 返回新链表的头结点

    最后你要返回的是"复制出来的那条链表"的头,而不是原来的。


用个小例子说明一下:

原链表(逻辑上):

  • 节点0:值 7,random -> null

  • 节点1:值 13,random -> 节点0

  • 节点2:值 11,random -> 节点4

  • ...

你要造一条新的链:

  • 新节点0:值 7,random -> 新节点里的 null

  • 新节点1:值 13,random -> 新节点0

  • 新节点2:值 11,random -> 新节点4

  • ...

结构关系一模一样,但"人"都换成新造的。

如果你现在明白题意了,下一步就是:怎么把旧节点和新节点对应起来并设置 random,这就是解法要做的事了。

核心难点

普通的链表拷贝只需要遍历并创建 next 节点即可。但这道题的难点在于 random 指针可能指向尚未创建的节点 ,或者指向前面已经过去的节点。

解决方案:哈希表法(最通用、易理解)

利用哈希表(unordered_map)建立 原节点地址 到 新节点地址 的映射关系。

  1. 第一轮遍历 :遍历原链表,只创建新节点(复制 val),并将 <原节点指针, 新节点指针> 存入哈希表。此时不处理指针连接。

  2. 第二轮遍历 :再次遍历原链表,通过哈希表找到对应的 新节点,并根据原节点的 next 和 random 指向,在哈希表中查找对应的新节点进行连接。

C++

复制代码
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

#include <unordered_map>

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if (head == nullptr) {
            return nullptr;
        }

        // 哈希表:Key是原节点地址,Value是新节点地址
        std::unordered_map<Node*, Node*> map;

        // 第一步:遍历原链表,创建新节点,并建立映射关系
        Node* cur = head;
        while (cur != nullptr) {
            map[cur] = new Node(cur->val);
            cur = cur->next;
        }

        // 第二步:再次遍历,构建新节点的 next 和 random 指针
        cur = head;
        while (cur != nullptr) {
            // map[cur] 是新节点
            // map[cur->next] 是原节点 next 指向的那个节点 对应的 新节点
            if (cur->next != nullptr) {
                map[cur]->next = map[cur->next];
            }
            
            // 同理处理 random
            if (cur->random != nullptr) {
                map[cur]->random = map[cur->random];
            }

            cur = cur->next;
        }

        // 返回哈希表中存储的 head 对应的新头节点
        return map[head];
    }
};

复杂度分析

  • 时间复杂度:O(N)。我们需要遍历链表两次,哈希表的查找操作是 O(1) 的,所以总时间是线性的。

  • 空间复杂度:O(N)。我们需要一个哈希表来存储 N 个节点的映射关系。

相关推荐
longlongzihan1 小时前
从LeetCode 70. 爬楼梯看动态规划:如何精准推导状态转移方程?
c++·算法·leetcode·动态规划
h_a_o777oah3 小时前
【树论】树链剖分:将树形结构转化为线性结构以处理树上路径问题(洛谷 P3384 附C++代码模板)
数据结构·算法·线段树·acm·树链剖分·树论·重链剖分
HEJOO93 小时前
指针的算术运算详解
java·数据结构·算法
Coder-magician3 小时前
《代码随想录》刷题打卡day47:并查集-part01
数据结构·算法·图论
垆边人似月.3 小时前
VLAN 资源池(100~200 分档)
数据结构·算法
程序员清风5 小时前
算法与数据结构入门:复杂度、数组与链表
数据结构·算法·链表
Rnan-prince7 小时前
LeetCode 股票买卖系列:一个动态规划状态机模板通吃四题
leetcode·动态规划
圣保罗的大教堂7 小时前
leetcode 3756. 连接非零数字并乘以其数字和 II 中等
leetcode
Logic1018 小时前
C语言/数据结构排序题解:三个数的最大乘积——排序后比较两种候选情况
c语言·数据结构·数组·排序·时间复杂度·算法题·负数处理
殷色玫瑰9 小时前
C++ string类详解:常用接口、字符串操作与模拟实现
java·linux·c语言·开发语言·数据结构·c++