LeetCode/solutions/384. Shuffle an Array.md
2019-12-19 17:34:31 +08:00

2.2 KiB
Raw Blame History

384. Shuffle an Array

思路

给定一个没有重复元素数组,将其随机打乱,即洗牌。

思路一、暴力

先看最暴力的解法,想象一下我们先将所有牌放到一个黑箱里面,然后不断从中不放回地随机抽取一张,这样最后得到的就是洗好的牌。

实现上我们可以用一个循环,第一循环时从整个数组中随机选一个元素然后将这个元素从数组中删掉;第二次循环再从中选出一个元素.....。

因为一共进行了n次循环每次将数组中的元素删除是线性复杂度的所以时间复杂度O(n^2)。

思路二、Fisher-Yates 洗牌算法

思路一每次循环的删除操作是线性复杂度的,可以优化至常数复杂度:我们每次将要删除的元素和最末尾的元素交换,然后删除末尾元素(其实不用删除,记录一下就是)。

优化后的暴力法就很类似Fisher-Yates洗牌算法了在每次迭代中生成一个范围在当前下标到数组末尾元素下标之间的随机下标。 接下来,将当前元素和随机选出的下标所指的元素互相交换(这一步模拟了每次从黑箱里面摸一张牌的过程),这样每次被随机选中的元素都不会再被选中(相当于已经删除了)。

注意:当前元素是可以和它本身互相交换的。

时间复杂度O(n)

C++

思路二

class Solution {
private:
    vector<int>nums_origin;
    vector<int>nums_shuffle;
    int size;
public:
    Solution(vector<int>& nums) {
        nums_origin = nums;
        nums_shuffle = nums;
        size = nums.size();
    }
    
    /** Resets the array to its original configuration and return it. */
    vector<int> reset() {
        return nums_origin;
    }
   
    /** Fisher-Yates 洗牌算法 */
    vector<int> shuffle() {
        // 反向迭代
        for(int i = size - 1; i >= 0; i--)
            swap(nums_shuffle[i], nums_shuffle[rand() % (i + 1)]);
        // 正向迭代
        // for(int i = 0; i < size; i++){
        //     int rand_idx = rand() % (size - i) + i;
        //     swap(nums_shuffle[i], nums_shuffle[rand_idx]);
        // }
        return nums_shuffle;
    }
};