一、问题描述
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
二、解题思路
在纸上画几个排列就能找到规律了,由于下一个排列肯定是基于后面几位来决定的,因此从后往前扫描数组。
先举几个例子 1,2,3,4 下一个排列1,2,4,3
1,2,4,3 下一个排列1,3,2,4
1,3,4,2 下一个排列1,4,2,3
1,4,3,2 下一个排列2,1,3,4
可以看出,从最后一个位置开始扫描,用一个指针index记录:
1)如果index的元素比index-1位置的元素大,交换index和index-1位置的元素
2)如果index的元素比index-1位置的元素小,更新index指向前一个位置,重复判断index和index-1,直到找到一个index,使得index-1位置的元素比index位置的元素小,标记这个index-1位置,记为pre
从最后一个位置往前扫描数组,直到找到一个比pre位置元素大的最小元素,由于pre右边的元素是递减的,因此从右往左扫描到的第一个比pre位置大的元素即为所求,记为suc。
交换pre和suc位置的元素,并对pre位置后的元素进行排序。
3)注意如果数组是降序排列的即任意位置index-1都比index大,说明没有下一个排列,直接对数组进行排序即可。
第2步的原理:如果数组中从某一位置index开始都是降序的,那么下一个排列一定会更新index-1位置的值,而这个index-1位置的新值一定是后面最小的比index-1位置原来的值大的数,因此交换这两个位置的元素。交换后,后面的元素一定是按照升序排列的,因此排序后面的数组。
三、代码
class Solution {
public void nextPermutation(int[] nums) {
if(nums.length==1)return;
int index=nums.length-1;
while(index>0 && nums[index-1]>=nums[index]){
index--;
}
if(index==0){
Arrays.sort(nums);
return;
}
int pre=index-1;//将要被交换的前面的元素
int suc=nums.length-1;//将要被交换的后面的指针
while(suc>pre&&nums[suc]<=nums[pre])suc--;
swap(nums,pre,suc);
Arrays.sort(nums,pre+1,nums.length);
}
public void swap(int[] nums,int i,int j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
四、结果
执行时间 | 1ms | 99.57% |
---|---|---|
消耗内存 | 40.3M | 43.75% |
五、总结
这道题目想清楚后也不难,在纸上画几个就能有规律了,主要还是要清楚排列变化的规则。
排列数组指定位置后面的元素的方法:
Arrays.sort(int[] nums,int begin,int end);
将nums数组从begin位置开始(包括begin)开始到end(不包括end)位置的元素进行排序。