设计问题-打乱数组

打乱一个没有重复元素的数组。

示例:

// 以数字集合 1, 2 和 3 初始化数组。
int[] nums = {1,2,3};
Solution solution = new Solution(nums);

// 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。
solution.shuffle();

// 重设数组到它的初始状态[1,2,3]。
solution.reset();

// 随机返回数组[1,2,3]打乱后的结果。
solution.shuffle();

Fisher-Yates 洗牌算法
在每次迭代中,获取一个在当前坐标到数组末尾区间的随机整数。接下来,将当前元素和随机选出的下标所指的元素互相交换,元素是允许和它自己交换

nums[] int           [1,2,3,4,5]   
i=0              随机坐标的区间就是[0,5)  假设是随机到坐标为1 那交换后变成 [2,1,3,4,5]
i=1              随机坐标的区间就是[1,5)  假设是随机到坐标还是1 那就和自己交换后变成 [2,1,3,4,5](其实就是没变)
i=2              随机坐标的区间就是[2,5)  假设是随机到坐标为4 那交换后变成 [2,1,5,4,3]
i=3              随机坐标的区间就是[3,5)  假设是随机到坐标为4 那交换后变成 [2,1,5,3,4]
i=4              随机坐标的区间就是[4,5)  只剩下一个坐标4了,因为区间是不包括5,直接和自己交换后变成 [2,1,5,3,4]

复杂度分析

  • 时间复杂度 : O(n)。Fisher-Yates 洗牌算法时间复杂度是线性的,因为算法中生成随机序列,交换两个元素这两种操作都是常数时间复杂度的。
  • 空间复杂度: O(n)。因为要实现 重置 功能,原始数组必须得保存一份,因此空间复杂度并没有优化。
type Solution struct {
   nums []int
   original []int //原始数组
}

//copy复制为值复制,改变原切片的值不会影响新切片。而等号复制为指针复制,改变原切片或新切片都会对另一个产生影响
func Constructor(nums []int) Solution {
    original := make([]int, len(nums))
    copy(original, nums)
    return Solution{original: original, nums: nums}


}


/** Resets the array to its original configuration and return it. */
func (this *Solution) Reset() []int {
    return this.original
}


/** Returns a random shuffling of the array. */
func (this *Solution) Shuffle() []int {
    len := len(this.nums)
    for index,_ := range this.nums  {
        //每次从index到数组末尾这个区间内随机获取一个数,和index做替换
        swapIndex := random(index,len)
        this.nums[index],this.nums[swapIndex] = this.nums[swapIndex],this.nums[index]
    }
    return this.nums
}

// 获取指定区间内的随机数
func random(min, max int) int{
    return rand.Intn(max-min)+min
}


/**
 * Your Solution object will be instantiated and called as such:
 * obj := Constructor(nums);
 * param_1 := obj.Reset();
 * param_2 := obj.Shuffle();
 */

你可能感兴趣的:(设计问题-打乱数组)