leetcode—数组—下一个排列

下一个排列

题目类型: 数组
题目来源: leetcode传送门

实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须 原地 修改,只允许使用额外常数空间。

示例1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例3:

输入:nums = [1,1,5]
输出:[1,5,1]

示例4:

输入:nums = [1]
输出:[1]

提示

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100

这题开始的想法是O(N2)的,从右侧遍历,假设当前遍历到了ai,那么如果在ai之前有一个比ai小的数字,则将这两个数字交换,然后再对i之后的进行升序排列。要满足的要求就是,被换的数字要尽可能靠右,这样才能保证是比当前字典序刚好大的字典序。这显然是个O(N2)的做法,但是这太暴力了,后来看了题解的方法,实在是太秒了,大概思路一样,但是优化了找到要交换的两个数字以及后面排序的方法,解法如下:

首先考虑一个问题,要是让被交换的数字尽可能靠右,我们应该怎么做,首先必须是从右侧遍历,这是毫无疑问的,那么下一步就是考虑如何用更少的次数去找到这两个数字。假设我们现在遍历到了ai,那么看ai-1的值,显然有两种情况,第一种:ai-1<ai,第二种:ai-1>=ai,对于第一种情况我们可以直接确定ai-1的后面一定有一个比ai-1大的数字,所以这时直接在后面找就可以了,这时循环也可以结束了,所以时间复杂度是O(N)的,对于第二种情况,由于再之前没出现过前一个数比后一个小的情况,所以在ai-1到最后是一个递减的序列,那么就没有必要在这里浪费时间,直接去判断ai-2和ai-1的关系即可。这样做的话,每个数字最多遍历两次,即找到一个ai-1<ai一次,再在ai-1之后找到最后的比ai-1大的数字一次,一共两次,到目前为止时间复杂度是O(N)。下一步就是对ai-1后面的序列进行一个升序排序,但是这个排序直接O(N)就可以完成,下面是原因以及做法。由于之前的信息可得到(假设ai-1<ai,aj是ai-1后面最右的比ai-1大 数字),有如下关系:ai-1<ai>=…>aj>=…且ai-1>=aj+1且aj-1>=aj>=ai-1,所以ai>=ai+1>=…>=aj-1>=ai-1>=aj+1>=…,所以在ai-1和aj交换后,后面是一个降序序列,只需要将后面的反转(reverse)就是升序序列了,所以在O(N)内就可以完成了,并且空间复杂度为O(1)reverse不会的可以看下面的相关代码

代码如下:

void reverse(vector<int>& nums, int begin, int end)
    {
        int temp;
        while(begin < end)
        {
            temp = nums[end];
            nums[end] = nums[begin];
            nums[begin] = temp;
            begin ++;
            end --;
        }
    }

    void nextPermutation(vector<int>& nums) {
        int temp;
        int left = -1;
        int right = -1;
        for(int i = nums.size() - 1; i >= 1; i --)
        {
            if(nums[i] > nums[i-1])
            {
                left = i-1;
                break;
            }
        }
        if(left != -1)
        {
            int temp = nums[left];
            for(int i = nums.size() - 1; i > left; i --)
            {
                if(nums[i] > temp)
                {
                    right = i;
                    break;
                }
            }
            nums[left] = nums[right];
            nums[right] = temp;
            reverse(nums, left + 1, nums.size() - 1);
        }
        else
        {
            reverse(nums, 0, nums.size() - 1);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值