LeetCode Hot100(58/100)——138. 随机链表的复制

文章目录

一、题目简介

给你一个长度为 n 的链表,每个节点包含两个指针:

  • next:指向下一个节点
  • random:指向链表中的任意节点或 null

要求你深拷贝这个链表,并返回复制后的链表头节点。

深拷贝意味着:

  • 新链表中的节点是全新创建的
  • nextrandom 的指向关系与原链表完全一致
  • 不能复用原链表的任何节点

二、节点结构说明

java 复制代码
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}

三、问题难点分析

与普通链表拷贝不同,本题的难点在于:

  1. random 指针可能:
    • 指向前面的节点
    • 指向后面的节点
    • 指向自身
    • null
  2. 拷贝时,必须确保 random 指向的是"新节点"而不是旧节点
  3. random 的指向关系在拷贝过程中并不具备天然的"先后顺序"

四、解法一:哈希表映射(最直观解法)

核心思想

旧节点 → 新节点,一一映射

使用 HashMap<Node, Node> 保存原节点与复制节点的对应关系。


解题步骤

第一步:复制所有节点(只处理 val)
text 复制代码
原链表: A -> B -> C
复制后: A' -> B' -> C'

建立映射关系:

复制代码
A  -> A'
B  -> B'
C  -> C'
第二步:处理 next 和 random 指针
  • newNode.next = map.get(oldNode.next)
  • newNode.random = map.get(oldNode.random)

过程示意(时序图)

新链表节点 HashMap 原链表节点 新链表节点 HashMap 原链表节点 put(OldNode, NewNode) get(OldNode.next) get(OldNode.random) 设置 next / random


Java 实现

java 复制代码
public Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }

    Map<Node, Node> map = new HashMap<>();

    // 1. 复制节点
    Node cur = head;
    while (cur != null) {
        map.put(cur, new Node(cur.val));
        cur = cur.next;
    }

    // 2. 复制指针
    cur = head;
    while (cur != null) {
        Node newNode = map.get(cur);
        newNode.next = map.get(cur.next);
        newNode.random = map.get(cur.random);
        cur = cur.next;
    }

    return map.get(head);
}

复杂度分析

指标 复杂度
时间复杂度 O(n)
空间复杂度 O(n)(HashMap)

优缺点总结

✅ 优点

  • 思路直观
  • 容易实现
  • 不易出错

❌ 缺点

  • 需要额外 O(n) 空间

五、解法二:原地复制(O(1) 额外空间,面试高频)

核心思想

把复制节点直接插入到原节点后面

利用链表结构本身,避免使用额外的哈希表。


整体流程概览

原链表
复制节点并插入原节点后
设置复制节点的 random 指针
拆分成原链表和新链表


步骤一:复制节点并插入

text 复制代码
原链表:
A -> B -> C

插入后:
A -> A' -> B -> B' -> C -> C'

示例图

A
A1
B
B1
C
C1


Java 实现(步骤一)

java 复制代码
Node cur = head;
while (cur != null) {
    Node copy = new Node(cur.val);
    copy.next = cur.next;
    cur.next = copy;
    cur = copy.next;
}

步骤二:设置 random 指针

关键观察

如果:

复制代码
cur.random = X

那么:

复制代码
cur.next.random = X.next

因为 X.next 正好是 X 的复制节点。


Java 实现(步骤二)

java 复制代码
cur = head;
while (cur != null) {
    if (cur.random != null) {
        cur.next.random = cur.random.next;
    }
    cur = cur.next.next;
}

步骤三:拆分链表

将交织在一起的链表拆分成:

  • 原链表
  • 新链表

Java 实现(步骤三)

java 复制代码
Node dummy = new Node(0);
Node copyCur = dummy;
cur = head;

while (cur != null) {
    copyCur.next = cur.next;
    cur.next = cur.next.next;

    copyCur = copyCur.next;
    cur = cur.next;
}

return dummy.next;

六、完整 Java 代码(O(1) 空间解法)

java 复制代码
public Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }

    Node cur = head;

    // 1. 复制节点并插入
    while (cur != null) {
        Node copy = new Node(cur.val);
        copy.next = cur.next;
        cur.next = copy;
        cur = copy.next;
    }

    // 2. 设置 random
    cur = head;
    while (cur != null) {
        if (cur.random != null) {
            cur.next.random = cur.random.next;
        }
        cur = cur.next.next;
    }

    // 3. 拆分链表
    Node dummy = new Node(0);
    Node copyCur = dummy;
    cur = head;

    while (cur != null) {
        copyCur.next = cur.next;
        cur.next = cur.next.next;

        copyCur = copyCur.next;
        cur = cur.next;
    }

    return dummy.next;
}

七、复杂度分析(原地解法)

指标 复杂度
时间复杂度 O(n)
空间复杂度 O(1)

八、两种解法对比总结

方案 时间复杂度 空间复杂度 难度 适用场景
HashMap O(n) O(n) ⭐⭐ 快速实现
原地复制 O(n) O(1) ⭐⭐⭐⭐ 面试高频
相关推荐
To_OC1 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC1 天前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
To_OC2 天前
LC 994 腐烂的橘子:人人都说是 BFS 入门题,我却写了三遍才过
javascript·算法·leetcode
To_OC3 天前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
To_OC3 天前
LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂
javascript·算法·leetcode
To_OC5 天前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
To_OC6 天前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
想吃火锅100513 天前
【leetcode】121.买卖股票的最佳时机js/c++
算法·leetcode·职场和发展
凌波粒13 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
疯狂成瘾者13 天前
Java 集合 LinkedList 详解:链表结构、常用方法和队列使用
java·开发语言·链表