31. Next Permutation #
题目 #
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
题目大意 #
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。必须 原地 修改,只允许使用额外常数空间。
解题思路 #
- 题目有 3 个问题需要解决。如何找到下一个排列。不存在下一个排列的时候如何生成最小的排列。如何原地修改。先解决第一个问题,如何找到下一个排列。下一个排列是找到一个大于当前排序的字典序,且变大的幅度最小。那么只能将较小的数与较大数做一次原地交换。并且较小数的下标要尽量靠右,较大数也要尽可能小。原地交换以后,还需要将较大数右边的数按照升序重新排列。这样交换以后,才能生成下一个排列。以排列 [8,9,6,10,7,2] 为例:能找到的符合条件的一对「较小数」与「较大数」的组合为 6 与 7,满足「较小数」尽量靠右,而「较大数」尽可能小。当完成交换后排列变为 [8,9,7,10,6,2],此时我们可以重排「较小数」右边的序列,序列变为 [8,9,7,2,6,10]。
- 第一步:在
nums[i]
中找到i
使得nums[i] < nums[i+1]
,此时较小数为nums[i]
,并且[i+1, n)
一定为下降区间。第二步:如果找到了这样的i
,则在下降区间[i+1, n)
中从后往前找到第一个j
,使得nums[i] < nums[j]
,此时较大数为nums[j]
。第三步,交换nums[i]
和nums[j]
,此时区间[i+1, n)
一定为降序区间。最后原地交换[i+1, n)
区间内的元素,使其变为升序,无需对该区间进行排序。 - 如果第一步找不到符合条件的下标
i
,说明当前序列已经是一个最大的排列。那么应该直接执行第三步,生成最小的排列。
代码 #
package leetcode
// 解法一
func nextPermutation(nums []int) {
i, j := 0, 0
for i = len(nums) - 2; i >= 0; i-- {
if nums[i] < nums[i+1] {
break
}
}
if i >= 0 {
for j = len(nums) - 1; j > i; j-- {
if nums[j] > nums[i] {
break
}
}
swap(&nums, i, j)
}
reverse(&nums, i+1, len(nums)-1)
}
func reverse(nums *[]int, i, j int) {
for i < j {
swap(nums, i, j)
i++
j--
}
}
func swap(nums *[]int, i, j int) {
(*nums)[i], (*nums)[j] = (*nums)[j], (*nums)[i]
}
// 解法二
// [2,(3),6,5,4,1] -> 2,(4),6,5,(3),1 -> 2,4, 1,3,5,6
func nextPermutation1(nums []int) {
var n = len(nums)
var pIdx = checkPermutationPossibility(nums)
if pIdx == -1 {
reverse(&nums, 0, n-1)
return
}
var rp = len(nums) - 1
// start from right most to leftward,find the first number which is larger than PIVOT
for rp > 0 {
if nums[rp] > nums[pIdx] {
swap(&nums, pIdx, rp)
break
} else {
rp--
}
}
// Finally, Reverse all elements which are right from pivot
reverse(&nums, pIdx+1, n-1)
}
// checkPermutationPossibility returns 1st occurrence Index where
// value is in decreasing order(from right to left)
// returns -1 if not found(it's already in its last permutation)
func checkPermutationPossibility(nums []int) (idx int) {
// search right to left for 1st number(from right) that is not in increasing order
var rp = len(nums) - 1
for rp > 0 {
if nums[rp-1] < nums[rp] {
idx = rp - 1
return idx
}
rp--
}
return -1
}