要求:
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
示例:
左边输入,右边输出:
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
思路:
- 改动数字应该是低位的较大的数,换到高位去,换完之后,更换的那个高位往后直至低位重新再排序,按从大到小排序即可
- 如12345:5是最低位,右边没有数字可以进行交换。然后看4,右边的5比4大,所以4,5互换即可,得到12354
- 如123654:从4开始往前找,4的右边没有数字可以与其交换。然后5,右边没有比它大的数字,然后6,右边没有更大的数字,然后3,可以与4互换,得到下一个更大的序列。
- 从低位开始,若
nums[i]>=nums[i+1]说明nums[i]更大,不做处理。不断递推,直至i=0,说明当前的排列是最大的,只能全部逆序输出最小的了。 - 若
nums[i]<nums[i+1]说明,nums[i]右边的数字有比它更大的,此时就需要一个循环去找到nums[i]右边中比它大的数字里面最小的那一个。而nums[i]右边的元素是递减排列的,所以依旧是从右往左找,找到第一个大于nums[i]的元素。for(;j>i && nums[j] <= nums[i]; j--),j就是用于交换的元素的索引 - 交换完之后,需要将i右边的元素全部重新排列,按升序排列,即
[i+1, nums.length-1]的排序。 - 升序排序中,交换过后的数组是降序排序的,所以用双指针原地交换即可。
代码:
class Solution {
public void nextPermutation(int[] nums){
int n = nums.length - 1;
for (int i = n-1; i > 0; i--) {
if (nums[i] > nums[i+1]) {
int j = n;
for(; i<j && nums[i]>nums[j]; j--);
exchange(nums, i, j);
reverse(nums, i+1, n);
return;//此处直接返回,是对于存在nums[i]>nums[i+1]的情况来说的
}
}
reverse(nums, 0, n);//当前序列是最大序列,直接逆序处理
}
public void exchange(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public void reverse(int[] nums, int i, int j) {
while (i < j) {
exchange(nums, i++, j--);
}
}
}