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 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
解:
- 要找到紧挨着当前字典序的字典序,也就是要尽可能小的改动。以1352463为例,最接近的是1352634。
- 1352463中,因为要尽可能小的改动,所以从指针从个位数开始看,4是从后往前数,第一次出现降序的数字,也就是说在更高位保持不变的情况下,只改变这一位,就可以使得字典序变大
- 如果初始数已经是最大字典序了,譬如54321,那么就不会找到出现降序的数字,跳过步骤4,直接进入步骤5的将指针右侧逆序
- 要找到当前位右边的数字中,刚好只比它大一点的数来交换位置。由步骤2的条件可知当前数右侧是降序的,所以直接从后往前遍历找到第一个比当前数大的即可。找到后交换位置
- 交换完毕后当前数右侧仍然保持降序(准确来说应该是非升序,因为会有相同的数)的性质。所以要把指针右侧(包含指针在内)逆序
const nextPermutation = function(nums) {
let idx = nums.length - 1
let smallNum = undefined
// 保证从idx往后是非升序
while (idx > 0) {
if (nums[idx] > nums[idx - 1]) {
smallNum = nums[idx - 1]
break
}
idx--
}
// 找到了一个左侧较小的数,没找到说明整个数组是非升序的
if (smallNum !== undefined) {
// 从往前找第一个比它大的数交换位置
for (let i = nums.length - 1; i >= idx; i--) {
if (nums[i] > smallNum) {
[nums[idx - 1], nums[i]] = [nums[i], nums[idx - 1]]
break
}
}
}
// 然后将idx及其右侧逆序
let idx2 = nums.length - 1
while (idx < idx2) {
[nums[idx], nums[idx2]] = [nums[idx2], nums[idx]]
idx++
idx2--
}
return nums
};