一、题目
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
二、思路
思路分析:
我们先理解一下 “下一个排列” 的定义:给定数字序列的字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
可以这样理解 字典序:把数字序列的一个排列看成一个整数,所有排列从小到大的顺序就是字典序。比如,序列 1,2,3,4,5,6,其排列依次为:
123456
123465
123546
...
654321
且有 123456 < 123465 < 123546 < … … <654321,这就是字典序。
这样我们就可以把原问题转化为:将给定的排列组合为一个整数,我们的目标就是对这些数字重新排列,找到下一个更大的整数。如 123 下一个更大的数为 132。如果没有更大的整数,则输出最小的整数。
怎么找下一个排列:
- 首先,怎么找更大的排列: 举个例子,要找到比 123456 更大的排列,只需要将后面一个较大的数(这里我们称它为 “大数”)和前面一个较小的数(这里我们称为 “小数”)交换即可,比如 6 和 5 交换得到 123465,6 和 4 交换得到 123654;
- 而我们要找的是下一个排列,也就是希望找到的下一个数增加的幅度尽可能的小,如上例我们要找的是 123465 而不是 123654。我们可以这样实现:
1、在尽可能靠右的低位进行交换,这样数字的增幅相对较小,这就需要从后往前查找;
2、将一个尽可能小的 “大数” 和它前面的 “小数” 交换。比如 123465,应该把 5 和 4 交换,而不是把 6 和 4 交换;
3、完成交换之后,需要将 “大数” 之后的所有数字置为升序,升序排列就是最小的排列。比如,123465 交换 5 和 4 之后得到 123564,将 “大数” 5之后的数字置为升序,得到 123546。显然,123546 < 123564,所以 123546 才是 123465 的下一个排列。
算法实现:
- 从后往前查找第一对相邻升序的元素,索引分别为 i 、 j i、j i、j ( i < j i < j i<j),且满足且 A [ i ] < A [ j ] A[i] < A[j] A[i]<A[j],如 123465 中的 46。此时, [ j , e n d ) [j,end) [j,end) 必然是降序的。 A [ i ] A[i] A[i] 就是上面分析中的 “小数”。
- 在 [ j , e n d ) [j,end) [j,end) 中从后往前查找第一个大于 A [ i ] A[i] A[i] 的数 A [ k ] A[k] A[k], A [ k ] A[k] A[k] 就是上面分析中的 “大数”。
- 交换 A [ i ] A[i] A[i] 和 A [ k ] A[k] A[k];
- 这时, [ j , e n d ) [j,end) [j,end) 仍然是降序的,将 [ j , e n d ) [j,end) [j,end) 置为升序;
- 如果在第一步找不到符合条件的相邻元素对,说明当前 [i,end) 为一个降序顺序,也就是不存在下一个更大的排列,则直接跳到步骤 4。
三、代码
class Solution {
public void nextPermutation(int[] nums) {
if(nums == null || nums.length < 2)
return;
int i = 0;
int j = nums.length - 1;
boolean flag = false;// 设置标志位,如果有下一个更大的排列将该位置为true
// 从左往右查找第一对相邻升序的元素
while(j != 0){
if(nums[j - 1] < nums[j]){
i = j - 1;
flag = true;
break;
}else{
j--;
}
}
if(flag){// 有下一个更大的排列
int k = nums.length - 1;
while(k != 0){
if(nums[i] < nums[k]){
swap(nums, i, k);
shengXu(nums, j, nums.length - 1);
break;
}else{
k--;
}
}
}else{// 没有下一个更大的排列,将序列置为升序
shengXu(nums, j, nums.length - 1);
}
}
public void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
// 把降序数组置为升序
public void shengXu(int[] nums, int start, int end){
while(start < end){
swap(nums, start, end);
start++;
end--;
}
}
}