题目
给你一个整数数组nums,设计算法来打乱一个没有重复元素的数组。
实现 Solution class
:
Solution(int[] nums)
使用整数数组nums
初始化对象int [] reset()
重设数组到它的初始状态并返回int [] shuffle()
返回数组随机打乱后的结果
思路
方法一:暴力法
对于 reset()
来说,可以初始化的时候拷贝一份 nums
,在调用reset()
时候直接返回拷贝的结果。
对于 shuffle()
来说,每次从剩下的元素中随机取一个数。
如何使得返回概率相同?
每次随机拿一个数(不放回),所有的数都会被等概率选中。
证明:
由上可以证明,不管哪一轮摸到哪一个数字,概率都是 1/n
。
class Solution:
def __init__(self, nums: List[int]):
self.nums = nums
self.original = nums[:]
def reset(self) -> List[int]:
"""
Resets the array to its original configuration and return it.
"""
return self.original
def shuffle(self) -> List[int]:
"""
Returns a random shuffling of the array.
"""
aux = self.nums[:]
for num in range(len(self.nums)):
remove_ind = random.randrange(0, len(self.nums)) # 随机返回range(0, len(self.nums))中的一个数
aux[num] = self.nums.pop(remove_ind)
self.nums = aux
return aux
# Your Solution object will be instantiated and called as such:
# obj = Solution(nums)
# param_1 = obj.reset()
# param_2 = obj.shuffle()
时间复杂度:O(n^2)。(list.pop(index)时间复杂度为O(n))
空间复杂度:O(n)
思路二:Fisher-Yates洗牌算法
如何优化呢?对于 shuffle()
操作,可以让数组中的元素相互交换,这样就避免了迭代修改数组的时间了。
如何模拟从数组中采样一个数不放回的操作?
- 迭代整个数组nums, i
- 在范围range(i, len(nums))中,随机生成一个下标j,交换nums[i]和nums[j];
class Solution:
def __init__(self, nums: List[int]):
self.nums = nums
self.original = nums[:]
def reset(self) -> List[int]:
"""
Resets the array to its original configuration and return it.
"""
return self.original
def shuffle(self) -> List[int]:
"""
Returns a random shuffling of the array.
"""
for i in range(len(self.nums)):
swap_ind = random.randrange(i, len(self.nums)) # 随机返回range(0, len(self.nums))中的一个数
self.nums[i], self.nums[swap_ind] = self.nums[swap_ind], self.nums[i]
return self.nums
# Your Solution object will be instantiated and called as such:
# obj = Solution(nums)
# param_1 = obj.reset()
# param_2 = obj.shuffle()
时间复杂度:O(n)
空间复杂度:O(n)