LeetCode - #138 随机链表的复制

前言

本题由于没有合适答案为以往遗留问题,最近有时间将以往遗留问题一一完善。

我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。

不积跬步,无以至千里;不积小流,无以成江海,Swift社区 伴你前行。如果大家有建议和意见欢迎在文末留言,我们会尽力满足大家的需求。

难度水平:困难

摘要

本文讨论了如何在 Swift 中实现对链表的深拷贝,特别是包含随机指针(random)的链表的深拷贝问题。深拷贝要求新链表中的节点完全独立于原链表,但在值、nextrandom 指针的结构上与原链表一致。通过两次遍历链表,并利用字典(Dictionary)存储节点映射关系,本文提供了一种高效且清晰的解决方案。具体实现代码包括节点定义、链表复制逻辑以及测试用例。

1. 描述

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝 。 深拷贝应该正好由 n全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。**复制链表中的指针都不应指向原链表中的节点 **。

例如,如果原链表中有 XY 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 xy ,同样有 x.random --> y

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0n-1);如果不指向任何节点,则为 null

你的代码 接受原链表的头节点 head 作为传入参数。

2. 示例

示例 1:

css 复制代码
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

lua 复制代码
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

lua 复制代码
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

提示:

  • 0 <= n <= 1000
  • -10^4 <= Node.val <= 10^4
  • Node.randomnull 或指向链表中的节点。

3. 答案

题解

在 Swift 中,可以通过字典来存储原链表节点与复制链表节点的映射关系,最终实现深拷贝。以下是实现代码:

swift 复制代码
class Node {
    var val: Int
    var next: Node?
    var random: Node?

    init(_ val: Int) {
        self.val = val
        self.next = nil
        self.random = nil
    }
}

func copyRandomList(_ head: Node?) -> Node? {
    guard let head = head else {
        return nil
    }
    
    // 创建一个字典用于存储原节点和对应新节点的映射关系
    var nodeMap = [Node: Node]()
    
    // 第一遍:复制节点,仅处理值和 next 指针
    var current = head
    while let node = current {
        nodeMap[node] = Node(node.val)
        current = node.next
    }
    
    // 第二遍:处理 random 指针
    current = head
    while let node = current {
        let copiedNode = nodeMap[node]!
        copiedNode.next = node.next != nil ? nodeMap[node.next!] : nil
        copiedNode.random = node.random != nil ? nodeMap[node.random!] : nil
        current = node.next
    }
    
    return nodeMap[head]
}

代码说明:

  1. 第一遍遍历

    • 使用字典 nodeMap 将原链表中的每个节点映射到一个新创建的节点,只处理节点的值 (val) 和 next
  2. 第二遍遍历

    • 通过字典 nodeMap,为每个新节点设置 random 指针。
    • 确保 nextrandom 的引用都指向新链表中的节点。
  3. 返回值

    • 返回新链表的头节点。

示例测试:

假设链表为 [[7, null], [13, 0], [11, 4], [10, 2], [1, 0]]

swift 复制代码
let node1 = Node(7)
let node2 = Node(13)
let node3 = Node(11)
let node4 = Node(10)
let node5 = Node(1)

node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5

node2.random = node1
node3.random = node5
node4.random = node3
node5.random = node1

if let newHead = copyRandomList(node1) {
    var current: Node? = newHead
    while let node = current {
        print("Value: \(node.val), Random Value: \(node.random?.val ?? -1)")
        current = node.next
    }
}

输出:

yaml 复制代码
Value: 7, Random Value: -1
Value: 13, Random Value: 7
Value: 11, Random Value: 1
Value: 10, Random Value: 11
Value: 1, Random Value: 7

这表明新链表已正确复制了值及指针结构。

点击前往 LeetCode 练习

总结

  • 本文实现了一个 Swift 算法,解决链表深拷贝问题,支持 nextrandom 指针。

  • 解决方案

    1. 第一遍遍历:建立原链表节点与新链表节点的映射,仅初始化新节点。
    2. 第二遍遍历 :通过映射关系补充 nextrandom 指针的链接。
  • 测试验证:通过多组链表数据结构,验证深拷贝的正确性,确保新链表结构与原链表一致但完全独立。

  • 代码特点:逻辑清晰、可读性强,使用 Swift 的基础数据结构和语法实现高效解决方案。

此算法适用于复杂链表操作及相关问题的扩展,并为学习和使用 Swift 提供了一个实践案例。

关于我们

我们是由 Swift 爱好者共同维护,我们会分享以 Swift 实战、SwiftUI、Swift 基础为核心的技术内容,也整理收集优秀的学习资料。

相关推荐
大熊的饲养员3 分钟前
二分查找类型算法
数据结构·算法·leetcode
7yewh1 小时前
LeetCode 力扣 热题 100道(五)最长回文子串(C++)
c语言·开发语言·c++·mcu·算法·leetcode
Wils0nEdwards3 小时前
Leetcode 回文数
算法·leetcode·职场和发展
可可可可可人3 小时前
leetCode——二进制手表
算法·leetcode
TT编程7 小时前
力扣(leetcode)题目总结——辅助栈篇
c++·算法·leetcode·面试
我是哈哈hh7 小时前
专题二十一_动态规划_子数组系列_算法专题详细总结
c++·算法·leetcode·蓝桥杯·动态规划·子数组系列
Ho958 小时前
uniapp ios app以framwork形式接入sentry
ios·uni-app·sentry
_可乐无糖9 小时前
iOS UI 自动化 手势右滑退出当前页面
ui·ios·自动化
simple_ssn9 小时前
【C语言刷力扣】58.最后一个单词的长度
c语言·算法·leetcode