31. 下一个排列(C++)

题干:
https://leetcode.cn/problems/next-permutation/

还能说什么呢?

//关于next_permutation(),请查阅资料
class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        next_permutation(nums.begin(), nums.end());
    }
};

自己写的算法:

思考:在拥有了一个排列之后我们应该如何得到下一个排列呢?
1、我们应该从后往前看。
2、如果我们只看最后两个数的话,很简单。
----如果这两个数是升序,那么这两个数就是最小排列,整个数组下一个排列就是前面数不变,最后两个数调换。
----如果这两个数是降序,那么这两个数是最大排列,那么我们就要更改(从后往前的)三个数(假设第三个数<倒数第二个数,如果后三个数都是降序我们就要借助倒数第四个数,以此类推)。我们要将倒数第三个数变成字典序中一个更大的数,算法就是我们要从后面两个数中选择大于倒数第三个数的数(这个数要尽可能小),然后将后面两个数改成升序(最小排列)。

总而言之:
先看最后两个数,如果是升序就改为降序。如果后两个数是降序,再加入倒数第三个数,如果还是降序,再加入倒数第四个数…直到破坏了后n个数的降序结构(n个数是降序,n+1个数不是降序)。此时我们要从后n个数中寻找大于倒数第n+1个数的最小的那个数,将它与第n+1个数调换,然后把后n个数变为升序(最小排列)。其实我们也可以把倒数第一个数当作降序,对应n == 1的情况,这样就统一了。

算法伪代码如下:

看后n个数(n == 1,2,3…n<数组size(),n == 1视作降序),如果第n+1个数破坏了降序结构,我们要从后n个数中寻找大于倒数第n+1个数的最小的那个数,将它与第n+1个数调换,然后把后n个数变为升序(最小排列)

降序区间不太准确,应该是非升序区间,因为有相等的元素。

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int left{ -1 };//破坏降序区间的位置
        int right{ static_cast<int>(nums.size() - 1) };// 闭区间 [right,nums.size()-1] 为降序区间
        
        //保证 right-1 不越界,寻找降序区间 (其实是非升序区间)
        while (right > 0 && nums[right-1] >= nums[right]) {
            right--;
        }
        
        
        if (right == 0) { }//整个数组都是降序的
        else {
            //把right == 0作为另一个分支,是因为这个分支可以避免left越界
            left = right - 1;
            //在降序区间寻找大于nums[left]的最小的那个,rightCopy不会越界,因为right!=0,最多rightCopy遍历完整个[right,nums.size()-1]区间,就肯定能找到
            int rightCopy{ static_cast<int>(nums.size() - 1) };
            while (nums[rightCopy] <= nums[left]) {rightCopy--; }
            swap(nums[left], nums[rightCopy]);//此时[right,nums.size()-1]是仍然是降序的,请思考原因  
        }
        //我们要把降序区间变为升序,前面已经说了[right,nums.size()-1]是仍然是降序的,所以可以调用reverse()。
        //这一步在if-else的两个分支中都要做,所以放在了这里
        reverse(nums.begin() + right, nums.end());
        //sort(nums.begin() + right, nums.end());也可以,但是感觉没有reverse()快
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值