31. Next Permutation
这道题思路如下:
1.从后往前找到第一个不是递增的数字的位置,记录下为p,如果p已经走到尽头,那么直接颠倒所有数字然后返回
2.从p往后找到比p大的最小的那个数的位置q
3.交换p,q的位置
4.对于p之后的所有的数颠倒位置
理由是这样的,如果一个数从后往前的数字都是递增的,那么这个数已经是排列出来的最大值了。
所以,我们现在的目标就是找到不是递增的那个最小的一位的数字x,这个数字是我们将要替换的。
因为到这个数字之后的数字,我们都已经遍历过了,是一个递减的数列,所以我们找到挨的最近的那个比x大最少的那个数字把x给替换掉,这样,就实现了把x换成了比x大的最小的数字,整个数字就变大了。
然后又是因为后面的数字是递增的,所以我们替换过去的位置也是排列好的,把颠倒就行了。
比如code ganker举得例子:2,3,6,5,4,1。
从后往前找到第一个不是递增的,也就是3这个位置。6541已经是这几个数字可以组成的最大的数字了,没有什么可以改进的地方,而3是可以改变的最低位,所以从3开始。6541已经是排序好的,所以我们要找到离3最近的,但是又比3大的数,也就是4。交换了3,4的位置,数字变成2,4,6,5,3,1。因为3取代的位置是,比3大的最小的数的位置,所以6531也是排列好的,也是这几个数字可以组成的最大数了。
由于高位被替换过了,也就是3被替换成4了,所以后面的数需要是可能的最小排列,而后面的数又是倒叙排过的,所以翻转过来就行。
代码如下:
1 public void nextPermutation(int[] nums) { 2 if(nums == null || nums.length <= 1) { 3 return; 4 } 5 int i = nums.length - 1; 6 while(i > 0 && nums[i-1] >= nums[i]) { 7 i--; 8 } 9 i--; 10 if(i == -1) { 11 reverse(nums, 0, nums.length - 1); 12 return; 13 } 14 int j = i+1; 15 while(j < nums.length && nums[j] > nums[i]){ 16 j++; 17 } 18 j--; 19 int temp = nums[i]; 20 nums[i] = nums[j]; 21 nums[j] = temp; 22 reverse(nums, i+1, nums.length - 1); 23 return; 24 } 25 26 private int[] reverse(int[] nums, int start, int end) { 27 int temp = nums[start]; 28 while(start < end) { 29 temp = nums[start]; 30 nums[start] = nums[end]; 31 nums[end] = temp; 32 start++; 33 end--; 34 } 35 return nums; 36 }
bug记录:
这个题目居然明明懂了原理,却错到我要吐…………每一行都是蠢人的血泪
1. 当length小于等于1的时候不需要处理
2. …………太多了算了= =!下次要注意就是了