0%

下一个排列

题目(LeetCode #31)

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

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

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

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。

1
2
3
1,2,3 -> 1,3,2
3,2,1 -> 1,2,3
1,1,5 -> 1,5,1

题解

参考题解

仿照 C++ 中 next_permutation 的实现方法。

分析

  1. 下一个数比当前数大:将后面的大数与前面的小数交换 ,如 123456 ,将 56 交换得到 123465
  2. 下一个数的增幅应尽量小,使之满足字典序中的 下一个,为了满足该条件需进行如下操作:
    1. 尽可能靠右的低位进行交换,需从后向前查找;
    2. 将一个尽可能小的大数与前面的小数交换,如 123465 ,下一个排列应该将 54 交换而不是将 64 交换;
    3. 大数换到前面后,需将大数后面的所有数重置为升序,升序排列即为最小排列。以 123465 为例:首先按照上一步,交换 54 ,得到 123564 。调整 5 后的数为升序,得到 123546 ,即为下一个排列。

实现步骤

  1. 从后向前查找第一个相邻升序的元素对 (i, j) ,满足 nums[i] < nums[j] 。此时 [j, end) 必然为降序;
  2. [j, end) 从后向前查找第一个满足 nums[i] < nums[k]knums[i]nums[k] 即为上述小数大数
  3. 交换 nums[i]nums[k]
  4. 此时 [j, end) 必然为降序,逆置 [j, end) 使其升序;
  5. 若在步骤 1 找不到符合的相邻元素对,则说明当前数组单调递减,直接跳到步骤 4,整体逆序即可。

图解

以求 12385764 的下一个排列为例:

  1. 初始时:

  2. 从后向前查找第一个相邻升序的元素对 (i, j) ,这里 i=4j=5 ,对应值为 57

  3. 从后向前查找第一个大于 nums[i] 的值 nums[k] ,此例中 nums[i] = 5 ,故 nums[k]=6

  4. 交换 nums[i]nums[k] ,即 56

  5. 交换后 [j, end) 必仍是降序,逆置 [j, end) ,使其变为升序:

  6. 12385764 的下一个排列为 12386457 ,逆序后对比如下:

实现

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public void nextPermutation(int[] nums) {
int arrLen = nums.length;
if (arrLen <= 1) return;

int i = arrLen - 2, j = arrLen - 1, k = arrLen - 1;

while (i >= 0 && nums[i] >= nums[j]) {
i--; j--;
}

if (i >= 0) {
while (nums[i] >= nums[k]) k--;
swap(i, k, nums);
}

for (i = j, j = arrLen - 1; i < j; i++, j--) {
swap(i, j, nums);
}
}

public void swap(int i, int j, int[] nums) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def nextPermutation(self, nums: list) -> None:
arr_len = len(nums)
if arr_len <= 1: return

i, j, k = arr_len - 2, arr_len - 1, arr_len - 1

while i >= 0 and nums[i] >= nums[j]:
i -= 1
j -= 1

if i >= 0:
while nums[i] >= nums[k]: k -= 1
nums[i], nums[k] = nums[k], nums[i]

i, j = j, arr_len - 1
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func nextPermutation(nums []int)  {
arrLen := len(nums)
if arrLen <= 1 {
return
}

i, j, k := arrLen - 2, arrLen - 1, arrLen - 1
for i >= 0 && nums[i] >= nums[j] {
i--
j--
}

if i >= 0 {
for nums[i] >= nums[k] {
k--
}
nums[i], nums[k] = nums[k], nums[i]
}

i, j = j, arrLen - 1
for i < j {
nums[i], nums[j] = nums[j], nums[i]
i++
j--
}
}