LeetCode 31. 下一个排列

112 阅读3分钟

目录:算法日记

题目来源:31. 下一个排列

题目描述

整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1] 。 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2]
  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2]
  • arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。 给你一个整数数组 nums ,找出 nums 的下一个排列。

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

题目示例

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

数据范围

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

算法思路

根据题意,需要保证两点:

  1. 下一个排列字典序大于 nums
  2. 下一个排序的字典序尽可能小

因此,尽可能的保证nums前部数字不变,从后向前寻找一个子排列,使得该子排列有更大的字典序。基于这一点,来看几种情况。

图表横坐标表示nums下标,纵坐标表示nums下标对应的数值。

先来考虑最特殊的情况,对于排列(子排列)[4,3,2,1]不存在比它更大的排列,此时,排列为降序。即对于降序排列,不存在字典序更大的排列方式。

1.png

在降序排列前插入一个数字,构造一个一般排列[2,4,3,2,1]

2.png

对于排列[2,4,3,2,1],从后向前寻找。子排列[4,3,2,1]降序,因此子排列不存在更大的字典序,继续向前寻找。对于子排列[2,4,3,2,1],该排列非降序,因此存在更大的字典序排列。接下来的问题就是如何调整该排列,在保证新排列字典序大于原字典序的同时,使得新排列字典序最小。

子排列[4,3,2,1]降序,因此仅能改变[2,4,3,2,1]中,第一个2的值,使其增大,且增量最小。此时,可选择的数据仅有降序排列的[4,3,2,1]。从后向前遍历降序排列,选取第一个大于2的数3可满足条件。交换23得到排列:nums1 = [3,4,2,2,1],该排列的字典序大于nums2 = [2,4,3,2,1]

3.png

观察两个排列,新排列nums1[0]的值已经大于原排列nums2[0]的值,要使其字典序最小,调整nums1后部的子排列,即将子序列降序改为子序列升序。得到的排列[3,1,2,2,4]。该排列满足字典序大于原排列,且字典序最小。

4.png

AC代码

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var nextPermutation = function(nums) {
    let i = nums.length - 1
    while(i > 0 && nums[i - 1] >= nums[i]) i--
    if(i === 0) nums.sort((a, b) => a - b)
    else {
        let j = nums.length - 1
        while(j >= i && nums[j] <= nums[i - 1]) j--
        [nums[i - 1], nums[j]] = [nums[j], nums[i - 1]]
        let l = i
        let r = nums.length - 1
        while(l < r) {
            [nums[l], nums[r]] = [nums[r], nums[l]]
            l++
            r--
        }
    }
};