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


分析

这道题也算是很经典的一道题了,问题是关于全排列的。简单来说就是给你一个序列,让你求出这个序列在全排列的下一个排列,如果是最后一个,那它的下一个就是第一个排列。

要能做出这题需要弄明白全排列的大小关系和对应的规律。我打算用具体的例子来分析。
首先我们需要明白的一点是全排列中,顺序的排列是最小的,而逆序的排列是最大的。举个例子:1234就是1、2、3、4四个数的最小全排列,原因就是按照从前到后的顺序,都是选的最小的数,对应的,4321就是这四个数的最大全排列。这个相信读者不难理解。
其实这两个例子就已经把全排列的大小关系给揭示了。更具体一点,我们来看两个相邻的排列,比如1234的下一个全排列,显然就是1243,因为它的下一个肯定是希望前面的数字尽量不变,而调整的是尽可能后的,这就暗示我们要找下一个全排列,那就要从后往前找满足条件的那个数,然后进行适当的调整,现在的问题是什么条件呢?又怎么调整呢?

我们再来看一个例子7 5 4 2 3 3 1,这个排列的下一个排列,显然就是[7,5,4,3,1,2,3],来分析一下,根据刚才所说,从后往前找,找到第一个满足条件的,我们依次看后面的部分,首先是一个1,这没什么好说的,然后是3 1,这两个数字形成了逆序,是最大的,我们无法调整了,接着往前,是3 3 1,这三个数字显然还是逆序,还是最大的,没办法调整,再往前,是2 3 3 1,这四个数的排列就不是逆序了,因为2比3小,这就意味着我们可以调整了,而对于2前面的部分,我们不管,因为到这里就已经不是最大的排列了。
那怎么改变呢,2 3 3 1的下一个排列是什么,因为2后面的都是逆序,是最大的,所以要换2,那用哪个数来换呢,肯定是比2大的,因为如果比2小那换了之后的排列比当前这个还小。那现在就只能拿3来换了,这个例子不太明显,如果是2 4 3 1,应该拿哪个换呢?显然是用3换,因为是选尽可能小的,毕竟我们是要求下一个相邻的排列。而这个3其实是2后面比2大的最小的那个数,而后面这些数都是逆序的,所以找这个换的数的做法就是从末尾往前找到第一个比2大的数
回到刚才的例子,换了之后就成了3 3 2 1,那现在是不是就结束了呢?很明显,3 3 2 1也不是这四个数的全排列最小的那个,因为要求第一个3后面的最小,而且后面的部分都是保证逆序的,所以我们最后只需要把第一个3后面的所有数逆置一下就可以了,也就是3 1 2 3,这时候就变成了7 5 4 3 1 2 3,这也是正确的答案。

上面的分析已经详细的把做法给描述出来了,下面再提炼一下:

1. 从后往前,找到第一个顺序的数对[x1,x2],注意如果到了序列首部还找不到,也就是整个序列是逆序的,那就直接转置整个序列;
2. 从后往前,找到第一个比x1大的数,记为x3;
3. 交换x1和x3,逆置x1后面的所有数。

代码:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        //从后往前 找到第一个顺序的 
        int i = nums.size() - 1;
        //注意等于号也得包含 比如 8 7 5 2 3 3 1 
        while (i >= 1 && nums[i] <= nums[i - 1]) i--;
        //完全逆序 已经是最大了 直接转置
        if(i == 0) {
            reverse(nums.begin(), nums.end());
            return;
        }
        //第一个顺序对的第一个数的位置记为pos
        int pos = i - 1;
        //接下来找到它后面仅大于它的那个数然后交换
        for (int j = nums.size() - 1; j >= i; j--) {
            if (nums[j] > nums[pos]) {
                swap(nums[j], nums[pos]);
                 //同时让pos之后所有元素为顺序
                reverse(nums.begin() + i , nums.end());
                return;
            }
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值