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

相关推荐
玖剹2 小时前
递归练习题(四)
c语言·数据结构·c++·算法·leetcode·深度优先·深度优先遍历
Mz12213 小时前
day04 小美的区间删除
数据结构·算法
小猪咪piggy3 小时前
【算法】day 20 leetcode 贪心
算法·leetcode·职场和发展
CoderYanger5 小时前
优选算法-优先级队列(堆):75.数据流中的第K大元素
java·开发语言·算法·leetcode·职场和发展·1024程序员节
希望有朝一日能如愿以偿5 小时前
力扣每日一题:能被k整除的最小整数
数据结构·算法·leetcode
Controller-Inversion5 小时前
力扣53最大字数组和
算法·leetcode·职场和发展
Dream it possible!5 小时前
LeetCode 面试经典 150_图_克隆图(90_133_C++_中等)(深度优先:DFS)
c++·leetcode·面试·
CoderYanger7 小时前
C.滑动窗口——1423. 可获得的最大点数
java·开发语言·算法·leetcode·1024程序员节