力扣刷题记录:384 打乱数组

本文讨论了三种数组元素洗牌的方法:暴力解法、其改进版通过预设索引避免无效选取,以及Knuth洗牌算法,后者通过交换元素实现高效洗牌。暴力解法易超时,改进版提高了效率,而Knuth算法的时间空间复杂度为O(n)。
摘要由CSDN通过智能技术生成

一. 暴力解法(超时)

        直接使用随机数和一个布尔数组进行数组元素的选取。布尔数组大小与输入数组相同,初始化为全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;
    }

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值