一. 暴力解法(超时)
直接使用随机数和一个布尔数组进行数组元素的选取。布尔数组大小与输入数组相同,初始化为全0,表示所有元素均未被选取过。进行多次循环,一旦选取某个未被选取过的下标就将其对应的数组元素加入nums2中,并将其对应的布尔数组元素设置为1。否则重新选择。
class Solution {
vector<int> nums1;
public:
Solution(vector<int>& nums) {
nums1 = nums;
}
vector<int> reset() {
return nums1;
}
vector<int> shuffle() {
vector<int> nums2;
int n = nums1.size();
vector<bool> flags(n,0);
for(int i=0;i<n;){
int index = rand()%(n-i);
if(!flags[index]) {nums2.push_back(nums1[index]);i++;flags[index]=1;}
else continue;
}
return nums2;
}
};
这种做法明显是容易超时的,尤其是当n较大,且 i 的大小比较靠近n时,极其容易出现循环很多很多次的情况。
二. 暴力解法改进版(时间超过6.69%C++用户)
暴力解法超时主要源于多次无效选取,而且无效选取的次数相当不稳定,有可能达到很多次。所以这次我们使用一个索引数组vector<int> index,将其第 i 个元素初始化为 i ,每次选取其中的索引,就将其删掉,以达到每一次选取都是有效选取的效果,代码如下:
vector<int> shuffle() {
vector<int> nums2;
int n = nums1.size();
vector<int> index;
for(int i=0; i<n; i++)
index.push_back(i);
for(int i=0;i<n;i++){
int x = rand()%(n-i);
nums2.push_back(nums1[index[x]]);
auto iter = index.begin();
for(int j=0;j<x;j++){
iter++;
}
index.erase(iter);
}
return nums2;
}
三 . Knuth洗牌算法(时间超过92.74%C++用户)
第1次循环,将索引x1对应的值与索引n-1的值进行交换;第二次循环,将索引x2对应的值与索引n-2的值进行交换,以此类推。时间空间复杂度均为O(n)。我个人觉得本质上仍然是保证每一次选取都是有效选取,只不过实现方法比暴力解法的改进版高明太多。代码如下:
vector<int> shuffle() {
vector<int> nums2 = nums1;
int n = nums1.size();
for(int i=0;i<n;i++){
int index = rand()%(n-i);
swap(nums2[index],nums2[n-1-i]);
}
return nums2;
}