持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。打乱后,数组的所有排列应该是 等可能 的。
实现 Solution class:
Solution(int[] nums):使用整数数组 nums 初始化对象int[] reset(): 重设数组到它的初始状态并返回int[] shuffle(): 返回数组随机打乱后的结果
思路
本题的核心在于 sheffle 方法能产生的所有排列情况都是等可能的。
所有的排列:
假设现在有 n 个不同的元素,我们现在对其进行排列。
- 第一个可以选择
1 ~ n中的某一个 - 然后第二次可以选择剩下
n - 1个中的某一个 - 依次选择直到最后一个元素。这样的话,排列的总可能数是
n * (n-1) * ... * 1,也就是!n
sheffle 返回的值是全部等可能中的一种,那么我们是否可以将初始化传入的数组 nums 进行全排列,然后从中随机选取一个呢?
这种方法是可行的,但是对于性能的损耗太大。我们可以对其进行优化。
通过上面的方式,我们很容易知道 n! = n * (n-1)!,相当于我们从 n 个元素中挑取了一个,剩下 n-1 个元素。挑取的方法有 n 种,剩下的 n-1 就是对其他元素的排列。
我们需要一种洗牌方案:
- 先等概率的从n个元素中挑选一个作为第一个元素
- 然后再对剩下的(n-1)个元素作类似的选择
function shuffle() {
for(let i=0; i<this.nums.length; i++) {
let temp // 交换元素的临时参数
// 题目中的数组长度不超过50,(this.nums.length - 1):每次从剩余的位置中取随机一个
let target = i + (Math.random() * 100) % (this.nums.length - i)
temp = this.nums[i]
this.nums[i] = this.nums[target]
this.nums[target] = temp
}
return this.nums
}
- 交换第一个:先随机交换数组中的第一个元素与其他(包含自己)元素,也就是
arr[0]与arr[0] ~ arr[n-1]都可能发生交换 - 交换第二个:随机交换
arr[1]与arr[1] ~ arr[n-1]中的一个 - 直到数组的最后一个值
var Solution = function(nums) {
this.nums = nums.slice() // slice 返回一个新数组
this.origin = nums.slice()
};
Solution.prototype.reset = function() {
this.nums = this.origin.slice()
return this.nums
};
Solution.prototype.shuffle = function() {
for(let i=0; i<this.nums.length; i++) {
let temp // 交换元素的临时参数
// 题目中的数组长度不超过50,(this.nums.length - 1):每次从剩余的位置中取随机一个
let target = i + Math.floor(Math.random() * 100) % (this.nums.length - i)
// 交换两个元素
temp = this.nums[i]
this.nums[i] = this.nums[target]
this.nums[target] = temp
}
return this.nums
};
\