LeetCode 384 打乱数组 Swift 题解:从洗牌算法到实际应用
文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 代码解析
- 示例测试及结果
- 时间复杂度
- 空间复杂度
- 总结
摘要
在日常开发中,我们经常会遇到需要“打乱顺序”的场景。比如:
- 推荐系统需要随机排列候选内容,避免总是相同顺序。
- 游戏发牌要做到公平,不能偏向某种结果。
- 随机抽题,保证每次的试卷题目顺序不一样。
这道题正是要求我们在给定一个数组的情况下,实现一个算法来 随机打乱数组,并且要保证所有排列出现的概率相同。本文会带你从题目描述开始,逐步拆解题解思路,给出 Swift 可运行的代码,并结合实际场景来理解这个问题。
描述
题目要求我们设计一个类 Solution
,支持以下操作:
- 初始化:用给定的数组初始化。
- reset():恢复到原始数组。
- 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}
}
代码解析
-
original
用来保存最初的数组,方便 reset() 时恢复。 -
array
是一个副本,用来进行打乱操作。 -
reset()
很简单,就是把array
重置成original
。 -
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 洗牌能保证所有排列等概率,这在实际应用中尤其重要。
比如:
- 在考试系统中,题目打乱要公平,不能出现某些排列概率偏大。
- 在游戏发牌中,要避免作弊嫌疑,必须做到完全等概率。
- 在推荐系统中,随机打乱可以避免“推荐疲劳”。
所以,这道题虽然看起来是个小玩意儿,但背后的思想在工程中非常实用。