算法设计与分析第十三次作业

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

题目要求找到由给出的数字所构成的新排列中比原排列大的最小的那个,也就是字典序的下一个元素,如果已经是最大的排列,则输出最小的排列

首先我们要想找到比现有排列大又最靠近的元素,我们考虑如果去改变更高位的元素,那么带来的数字变化肯定会比只动更低位的数字要大,所以我们肯定是要尽可能的去改变低位的数字,而保留原有序列的高位,然而并不是所有的低位都可以改变,如果从a[k] 到 a[n-1]是一直降序的,那么我们就没有办法用改变a[k]到a[n-1]间的元素来产生一个比原来的数字更大的数,所以我们一开始的思路就比较清晰了,就是从后往前找,找到第一个不满足从a[k]到a[n-1]为降序的k,如果找到数组头都没有找到,那说明整个数组都是降序的,那么只要把整个数组颠倒过来就可以得到最小的序列了,而如果找到了这样的k,那么因为a[k]到a[n-1]并不是降序的,我们一定可以通过排列这些元素来产生一个更大的数,而又因为a[k-1]到a[n-1]是降序的,所以我们a[k]肯定是要改变的,而k的位置要由谁来替代呢?首先这个数肯定不能比k小,否则重排列后的数字肯定就要比原来的数小了,所以我们就从a[k-1]到a[n-1]中从后往前扫描找到一个大于a[k]的,进行交换,这样交换后的数就一定大于原来的了,注意在这里找不到是不可能的,否则a[k]到a[n-1]就也是降序的了,然后我们注意我们要找最近的那个排列,而既然a[k]确定后已经可以确定新排列比旧排列要大了,那a[k-1]到a[n-1]肯定是越小越好,而a[k]虽然是被换掉了,但是交换后的序列肯定还是满足a[k-1]~a[n-1]是降序的,因为a[k]在它的新位置肯定比右边的元素大,否则就会选右边的元素来交换了,而肯定比左边的元素小,因为被换出的元素本来就比左边的小,而a[k]比被换出的那个数还要小。既然满足降序那么问题就变得很简单了,只需要把降序的部分颠倒过来即可,最终的代码实现如下:

class Solution
{
public:
    void nextPermutation(vector<int> &nums)
    {
        int length = nums.size();
        int i = length - 1;
        //找非降序序列
        while(i > 0 && nums[i] <= nums[i - 1])
        {
            i--;
        }
        //如果i的值不为0,那么说明找到了,但实际交换的数是它的前一个
        if(i)
        {
            i--;
        }
        else
        {
        	//整个序列都是降序的,颠倒即可
            int i = 0;
            int j = length - 1;
            while(i < j)
            {
                swap(nums[i], nums[j]);
                i++;
                j--;
            }
            return;
        }
        //找到比nums[i]大的最小的数,并和nums[i]交换
        int j = length - 1;
        while(i < j && nums[j] < nums[i])
        {
            j--;
        }
        swap(nums[j], nums[i]);
        //把倒序序列颠倒过来
        i++;
        j = length - 1;
        while(i < j)
        {
            swap(nums[i], nums[j]);
            i++;
            j--;
        }
    }
};

时间复杂度为O(n),空间复杂度为O(1),n为数组的长度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值