当前位置: 首页 > news >正文

LeetCode 384 打乱数组 Swift 题解:从洗牌算法到实际应用

在这里插入图片描述
在这里插入图片描述

文章目录

    • 摘要
    • 描述
    • 题解答案
    • 题解代码分析
      • 代码解析
    • 示例测试及结果
    • 时间复杂度
    • 空间复杂度
    • 总结

摘要

在日常开发中,我们经常会遇到需要“打乱顺序”的场景。比如:

  • 推荐系统需要随机排列候选内容,避免总是相同顺序。
  • 游戏发牌要做到公平,不能偏向某种结果。
  • 随机抽题,保证每次的试卷题目顺序不一样。

这道题正是要求我们在给定一个数组的情况下,实现一个算法来 随机打乱数组,并且要保证所有排列出现的概率相同。本文会带你从题目描述开始,逐步拆解题解思路,给出 Swift 可运行的代码,并结合实际场景来理解这个问题。

描述

题目要求我们设计一个类 Solution,支持以下操作:

  1. 初始化:用给定的数组初始化。
  2. reset():恢复到原始数组。
  3. shuffle():返回数组的随机打乱结果,并且所有结果的概率相同。

举个例子:

let solution = Solution([1, 2, 3])
print(solution.shuffle()) // 可能是 [3,1,2] 或 [2,3,1] 等
print(solution.reset())   // 必须返回 [1,2,3]
print(solution.shuffle()) // 又可能是 [1,3,2] 或其他

这就像是发扑克牌一样,你必须保证每次洗牌后的结果是公平的。

题解答案

实现这个需求,核心就是要用 Fisher-Yates 洗牌算法(又叫 Knuth Shuffle)。

它的思路很简单:

  • 从数组的第一个元素开始,每次从后面的元素里随机挑一个,交换位置。
  • 这样可以确保每种排列出现的概率相同。

这个方法比“直接随机重排”更高效,因为我们只需要 O(n) 的时间。

题解代码分析

下面是完整的 Swift 实现:

import Foundationclass Solution {private var original: [Int]private var array: [Int]init(_ nums: [Int]) {self.original = numsself.array = nums}func reset() -> [Int] {array = originalreturn array}func shuffle() -> [Int] {var shuffled = arrayfor i in 0..<shuffled.count {let j = Int.random(in: i..<shuffled.count)shuffled.swapAt(i, j)}return shuffled}
}

代码解析

  1. original 用来保存最初的数组,方便 reset() 时恢复。

  2. array 是一个副本,用来进行打乱操作。

  3. reset() 很简单,就是把 array 重置成 original

  4. shuffle() 里用的是 Fisher-Yates 洗牌

    • 遍历数组索引 i。
    • 随机选择一个 j ∈ [i, count-1]
    • 交换 shuffled[i]shuffled[j]

这样保证了每次打乱都是公平的。

示例测试及结果

我们写一个 Demo 来看看效果:

let solution = Solution([1, 2, 3])print("第一次 shuffle:", solution.shuffle())
print("reset 后:", solution.reset())
print("第二次 shuffle:", solution.shuffle())
print("第三次 shuffle:", solution.shuffle())

可能的输出结果是:

第一次 shuffle: [2, 1, 3]
reset 后: [1, 2, 3]
第二次 shuffle: [3, 1, 2]
第三次 shuffle: [1, 3, 2]

每次 shuffle() 的结果都可能不同,这正是题目要求的“等概率随机”。

时间复杂度

  • reset():O(1),直接返回原数组。
  • shuffle():O(n),因为我们要遍历数组并交换。

整体非常高效,即使调用上万次也能轻松运行。

空间复杂度

  • 需要存储一份原始数组 original,所以是 O(n)
  • 其他只用了常量额外空间。

总结

这道题本质上是考察 随机算法 的正确性与公平性。
很多人一开始可能会想到“用系统自带的 shuffle 方法”,但关键在于理解 为什么 Fisher-Yates 洗牌能保证所有排列等概率,这在实际应用中尤其重要。

比如:

  • 在考试系统中,题目打乱要公平,不能出现某些排列概率偏大。
  • 在游戏发牌中,要避免作弊嫌疑,必须做到完全等概率。
  • 在推荐系统中,随机打乱可以避免“推荐疲劳”。

所以,这道题虽然看起来是个小玩意儿,但背后的思想在工程中非常实用。

http://www.dtcms.com/a/393532.html

相关文章:

  • 计算机网络-因特网
  • HDFS和MapReduce——Hadoop的两大核心技
  • 【华为OD】石头剪刀布游戏
  • LinuxC++项目开发日志——基于正倒排索引的boost搜索引擎(1——项目框架)
  • Photoshop - Photoshop 非破坏性编辑
  • C++入门小馆:C++11第三弹(可变参数模板)
  • 常用设计模式中的工厂模式,责任链模式,策略模式和单例模式的简单说明
  • aave v3 合约解析1 存款
  • autosar中自旋锁和互斥锁的应用
  • 建筑可视化告别“假”渲染:用Photoshop+Twinmotion打造照片级场景
  • 一键生成linux服务器健康巡检html报告
  • 数据结构(C语言篇):(十八)交换排序
  • Ubuntu20.04下跑通ORB-SLAM2
  • C++二进制转十进制
  • WordPress用户系统 + JWT认证:打造统一的应用登录解决方案
  • PortSwigger靶场之将反射型 XSS 注入到带有尖括号和双引号的 JavaScript 字符串中,并使用 HTML 编码和单引号进行转义通关秘籍
  • win11电脑按键失灵,提供几个可能恢复的方法
  • Android 中获取稳定时间的方法
  • mac编译ffmpeg
  • Deepsoil V7.1.10+Shake2000,最新版程序、教学视频、PDF使用手册
  • Apollo相机数据RTMP推流与播放指南
  • 使用Python扩展Unity编辑器
  • 【Android】自定义控件
  • 探索 Event 框架 6:高级特性与性能优化
  • JavaSE基础——第九章 枚举类注解
  • 云计算在金融领域中的应用
  • 【入门算法】前缀和:先预存再求和,以空间换时间
  • mac编译vst3sdk
  • Java 网络原理(二)--- TCP的机制 + IP协议 +以太网协议 + DNS
  • Python文件名编码处理深度解析:绕过编码问题的专业指南