LeetCode:31. Next Permutation

题目:

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place and use only constant extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

大意是将数字重新排列组合,使之成为比现有数大的数中最小的那个。

这题有点饶人,需要自己多举几个例子演算一下。
下面是我总结出的步骤:
1)找到符合(1)m < n (2) nums[m] < nums[n]这样的m,n组合,找到这样的组合中m最大的一个(m和n都是索引)。如果没有这样的组合,说明数字都是降序排列,按照题目要求,将整个数组倒过来即可
2)然后符合上述两个情况的n中,找到nums[n]最小的一个,将nums[m]和nums[n]进行swap
3)对m之后的元素进行升序排序

下面是代码:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {//找倒序
        if (nums.size() < 2)
            return;
        using std::swap;
        int m, n;
        bool found_m = false;
        m = 0;
        for (int i = nums.size()-1; i >= m; --i) { //注意这里的i >= m
            for (int j = i-1; j >= m; --j) //这里的j >= m,因为至少要>=现有的m,才有比较的意义
                if (nums[j] < nums[i]) {
                    found_m = true;
                    m = j;
                }
        }
        if (!found_m) {
            reverse(nums);
            return;
        }
        int nVal_min = INT_MAX;
        for (int i = m+1; i < nums.size(); ++i) 
            if (nums[m] < nums[i] && nums[i] < nVal_min){
                nVal_min = nums[i];
                n = i;
            }
 
        swap(nums[m], nums[n]);
        sort(nums.begin()+m+1, nums.end());
        
        return;
    }
    
private:
    void reverse(vector<int>& nums) {
        using std::swap;
        auto sz = nums.size();
        for (int i = 0; i < sz/2; ++i)
            swap(nums[i], nums[sz-1-i]);
    }
};

看了评论区的答案之后,对步骤做一定的优化:
其实,m后面的数字都是非升序排列。要找到大于num[m]且最小的那个数,其实就是从最后开始向前找到第一个比它大的数即可。此时交换它们。然后按照上面的做法是重新排序,但是我们需要了解,因为这次交换没有改变非升序的性质,所以重新排序只需要reverse一下就行了。所以:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int sz = nums.size();
        if (sz <= 1)
            return;
        int firstSmallerIdx = sz-2;
        while (firstSmallerIdx >= 0) {
            if (nums[firstSmallerIdx] < nums[firstSmallerIdx+1])
                break;
            --firstSmallerIdx;
        }
        if (firstSmallerIdx < 0) {
            reverse(nums.begin(), nums.end());
            return;
        }
        /*
        int nextBiggerNum = INT_MAX;
        int nextBiggerIdx = firstSmallerIdx+1;
        for (int i = firstSmallerIdx+1; i < sz; ++i) {
            if (nums[i] > nums[firstSmallerIdx] && nums[i] < nextBiggerNum) {
                nextBiggerNum = nums[i];
                nextBiggerIdx = i;
            }
        }
        swap(nums[firstSmallerIdx], nums[nextBiggerIdx]);
        sort(nums.begin()+firstSmallerIdx+1, nums.end());
        */
        int nextBiggerIdx = sz-1;
        while (nums[nextBiggerIdx] <= nums[firstSmallerIdx])
            --nextBiggerIdx;
        swap(nums[firstSmallerIdx], nums[nextBiggerIdx]);
        reverse(nums.begin()+firstSmallerIdx+1, nums.end());
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值