LeetCode:31. Next Permutation

31. Next Permutation

Medium

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

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

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

Example 1:

Input: nums = [1,2,3]
Output: [1,3,2]

Example 2:

Input: nums = [3,2,1]
Output: [1,2,3]

Example 3:

Input: nums = [1,1,5]
Output: [1,5,1]

Example 4:

Input: nums = [1]
Output: [1]

Constraints:

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

 

解法:

首先找下规律——到底什么样的一串数字才是一个全排列的下一个组合?以[1, 2, 3]为例,全排列的顺序组合是

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
​​​​​​​​​​​​​​[3, 2, 1]

从​​​​​​​​​​​​​​[1, 2, 3]变到[1, 3, 2]的过程和从[1, 3, 2]变到[2, 1, 3]的过程,其实就是从右往左找第一个比最后一位数字小的数字,并将最后一位与之交换,然后将其余位上的数字(包括数组的最后一位)掉头翻转的过程。比如从[1, 3, 2]变到[2, 1, 3]。首先是从右往左找到第一个比2小的数字,那么就是这里就是1。然后1和2交换,变成[2, 3, 1],然后将3和1顺序对调即可。但是这个规律有个问题,就是如果给出的数字里包含重复的数字,那么这个规律就不成立了。比如,考虑数组[1,2,3,1],它的下一组数字应该是[1,3,1,2],但是按照我们之前的算法,下一组就变成了[1,1,2,3],显然结果不对。那么以[1,1,2,3]为例再找下规律,

[1,1,2,3]
[1,1,3,2]
[1,2,1,3]
[1,2,3,1]
[1,3,1,2]
...

好像还是看不出什么规律,那么继续放大数组,以[1,1,2,3,  4]为例
[1,1,2,3,4]
[1,1,2,4,3]
​​​​​​​[1,1,3,2,4]
​​​​​​​[1,1,3,4,2]
​​​​​​​​​​​​​​[1,1,4,2,3]
​​​​​​​​​​​​​​[1,1,4,3,2]
​​​​​​​​​​​​​​[1,2,1,3,4]
​​​​​​​​​​​​​​​​​​​​​[1,2,1,4,3]
...
​​​​​​​冥冥之中,我们隐约可以感觉到一个规律,一个全排列组合的数字其实可以分成两部分,在数组的某一位之前的数字都是递增的,该位之后的数字都是递减的;然后如果从头到尾都是递增的,则代表该数组是全排列的第一组组合方方式;反之从头到尾都是递减的,则代表该数组是全排列的最后一组组合方式。以​​​​​​​[1,1,3,4,2]为例,4之前都是递增的,4之后都是递减的。那么从​​​​​​​[1,1,3,4,2]变到​​​​​​​​​​​​​​[1,1,4,2,3]的过程就是4与3发生了顺序上的对调,而后3与2又发生了一次对调。不同的是4,3,2这三位数的对调后的位置不是在原位。那么变换后的位置有何规律呢?(这点比较难想,只能靠观察和不断的尝试)

其实这个规律就是首先从右往左寻找到数组开始变成递减的位置,那么从数组的右往左,2 -> 4 -> 3,显然4是分界岭。那么它的前一位是3,我们记住这个数字,然后从3位置的下一位起寻找最后一个比它大的数字,也就是4,然后将3与4对调,形成​​​​​​​​​​​​​​[1,1,4,3,2],然后把4之后的数字翻转对调即可。那么这个规律的普遍算法就是
1)从右往左找数组开始递减的那一位的前一位,记为i
2)从i+1位开始寻找第一个比a[i]大的数字,记为a[j]
3)交换a[i]和a[j]
4)将a[j+1]到a[len - 1]之间的数字翻转即可

代码:

public static void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        while (i >= 0 && nums[i + 1] <= nums[i]) {
            i--;
        }
        if (i >= 0) {
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            swap(nums, i, j);
        }
        reverse(nums, i + 1);
    }

    private static void reverse(int[] nums, int start) {
        int i = start, j = nums.length - 1;
        while (i < j) {
            swap(nums, i, j);
            i++;
            j--;
        }
    }

    private static void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yexianyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值