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) ⭐⭐⭐⭐ 面试高频
相关推荐
Frostnova丶2 小时前
LeetCode 868. 二进制间距
算法·leetcode
WZ188104638692 小时前
LeetCode第54题
算法·leetcode
丰海洋3 小时前
Leetcode-hot100-136只出现一次的数字
算法·leetcode·职场和发展
We་ct3 小时前
LeetCode 124. 二叉树中的最大路径和:刷题解析
前端·数据结构·算法·leetcode·typescript
追随者永远是胜利者11 小时前
(LeetCode-Hot100)253. 会议室 II
java·算法·leetcode·go
程序员酥皮蛋12 小时前
hot 100 第三十五题 35.二叉树的中序遍历
数据结构·算法·leetcode
追随者永远是胜利者12 小时前
(LeetCode-Hot100)207. 课程表
java·算法·leetcode·go
望舒51316 小时前
代码随想录day33,动态规划part2
java·算法·leetcode·动态规划
追随者永远是胜利者16 小时前
(LeetCode-Hot100)169. 多数元素
java·算法·leetcode·go