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

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

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

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

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

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

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

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

关键点有三个:

  1. 每个节点有两个指针

    • next:正常链表的下一个

    • random:可以指向

      • 链表里的任意一个节点

      • 或者是 null

  2. 要做的是"深拷贝"

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

    • 要重新 new 出 n 个新节点

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

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

  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. 第二轮遍历 :再次遍历原链表,通过哈希表找到对应的 新节点,并根据原节点的 nextrandom 指向,在哈希表中查找对应的新节点进行连接。

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 个节点的映射关系。

相关推荐
向哆哆6 分钟前
构建跨端健身俱乐部管理系统:Flutter × OpenHarmony 的数据结构与设计解析
数据结构·flutter·鸿蒙·openharmony·开源鸿蒙
We་ct34 分钟前
LeetCode 54. 螺旋矩阵:两种解法吃透顺时针遍历逻辑
前端·算法·leetcode·矩阵·typescript
独自破碎E1 小时前
【总和拆分 + 双变量遍历】LCR_012_寻找数组的中心下标
数据结构·算法
txzrxz2 小时前
结构体排序,双指针,单调栈
数据结构·算法·双指针算法·单调栈·结构体排序
wWYy.2 小时前
算法:二叉树最大路径和
数据结构·算法
We་ct2 小时前
LeetCode 36. 有效的数独:Set实现哈希表最优解
前端·算法·leetcode·typescript·散列表
一条大祥脚2 小时前
ABC357 基环树dp|懒标记线段树
数据结构·算法·图论
tod1132 小时前
力扣高频 SQL 50 题阶段总结(四)
开发语言·数据库·sql·算法·leetcode
苦藤新鸡2 小时前
50.腐烂的橘子
数据结构·算法
无限进步_3 小时前
面试题 02.02. 返回倒数第 k 个节点 - 题解与详细分析
c语言·开发语言·数据结构·git·链表·github·visual studio