分享一道奇妙的力扣题---下一个排列

31. 下一个排列 - 力扣(LeetCode)

 我们要做的就是让左边一个较小的数与右边一个较大的数进行交换,而且,要让 ”较小的数尽可能靠近右边,而较大的数尽可能的小“

而且再交换之后,要让i到n-1上的数字从小到大,这一点很重要,只有这样才能保证变大的幅度最小

 越靠右的话改变的位置越小,十位的变动肯定比百位,千位引起的变化小的多

改变的时候,肯定是想又能变大,又尽可能的变大的比较少

有了这些基础我们就可以来写这道题了:

我们先从后向前,去找第一个出现nums[ i-1 ] < nums[ i ] 的情况,那么在i到n-1的区间上,肯定是降序的,因为都不满足前面的条件嘛,所以说我们可以从后面的区间   i 到 n-1上 从后向前去找到到第一个元素j,满足nums[ i-1 ]  < nums[ j ],然后交换这两个数的值

交换之后这个这个区间也还是降序的,我们只需要翻转reverse一下就能得到升序,这个复杂度也是O(n),如果用sort的话复杂度是O(nlogn)。外面一次遍历是O(n),内部一次翻转,这两个是并列的,相当于最多进行两次遍历,也就是2 * O(n),也就是说时间复杂度能够达到O(n),实在是妙哉!

//两次扫描
    void nextPermutation(vector<int>& nums) {
        int n=nums.size();
        for(int i=n-1;i>0;--i){
            if(nums[i-1]<nums[i]){//找到第一个i-1 < i的位置
                //i到n-1上肯定是降序
                //从i到n-1上从后向前找到第一个满足nums[i-1]<nums[j]的数,这个数就是这里面最小的,而且大于nums[i-1]的数,交换以后这个区间还是降序的,reverse之后就能满足升序
                for(int j=n-1;j>=i;--j){
                    if(nums[i-1]<nums[j]){
                        int tmp=nums[i-1];//交换
                        nums[i-1]=nums[j];
                        nums[j]=tmp;
                        //翻转    i 到 n-1 这个区间,就能让这个区间升序
                        //sort(nums.begin()+i,nums.end())
                        reverse(nums.begin()+i,nums.end());
                        return;
                    }
                }
            }
        }
        //如果没有返回则说明需要从到到尾翻转
        reverse(nums.begin(),nums.end());
    }

当我们用sort的时候:

而我们用了reverse的话:

 

 可以看到,reverse是真的秒啊

猜你喜欢

转载自blog.csdn.net/flyingcloud6/article/details/129856511
今日推荐