题目
Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
The replacement must be in-place, do not allocate extra memory.
Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
public class Solution {
public void nextPermutation(int[] nums) {
}
}
Leetcode链接
https://leetcode.com/problems...
算法
我们分几种情况讨论这个问题。
当数组
nums
的长度为0
或1
时,直接返回即可。当数组
nums
的长度为2
时,直接交换这两个数字的位置即可。-
当数组
nums
的长度为大于等于3
时,我们再分几种情况进行讨论。当数组nums呈非正序排列时,如
[5, 4, 3, 2, 2, 1]
,此时整个数组所组成的数字一定是最大的,我们把整个数组倒过来,变成[1, 2, 2, 3, 4, 5]
即可得到组成数字最小的组合,也就是我们所需要的下一个排列组合。-
当数组nums不符合上述情况时,数组中就一定存在一个数字
nums[i]
使得nums[i] < nums[i + 1]
。我们从数组的最后一个数字往前数,直到我们发现数字nums[i] < nums[i + 1]
为止,假设现在我们要处理的数组为[5, 2, 3, 4, 8, 7, 2]
,那么我们就从最后一个数字开始数:7 < 2
? 明显不成立,继续。8 < 7
? 明显不成立,继续。4 < 8
? 成立,停止检测,数字4
的位置为3
, 也就是说i = 3
。
现在我们得到了
i
的值,我们还需要确定一个结论,那就是在这种情况下,我们得到的下一个排列组合一定是以nums[0], nums[1], nums[2], ..., nums[i - 1]
开头(证明见附1),在我们的例子里就是以5, 2, 3
开头。因为此时nums[i + 1], nums[i + 2], ..., nums[nums.length - 1]
所组成的数组已经最大了,所以我们要找下一个比nums[i]
大的数字,将它与nums[i]
进行交换,然后使得nums[i + 1], nums[i + 2], ..., nums[nums.length - 1]
最小即可,这就是我们所需要的下一个排列组合,因为它就是下一个比原来的nums
大的数。为了得到这个数字,我们所需要做的就是从
nums[i + 1]
开始,一直到nums[nums.length - 1]
为止,寻找第一个小于等于nums[i]
的数,我们称这个数为nums[j]
,那么我们交换nums[i]
和nums[j - 1]
(上例交换之后的结果为[5, 2, 3, 7, 8, 4, 2]
),然后将nums[i + 1], nums[i + 2], ..., nums[nums.length - 1]
倒序排列(上例倒序排列的结果为[5, 2, 3, 7, 2, 4, 8]
)即可。
Java代码:
public class Solution {
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
private void reverse(int[] nums, int start) {
for(int i = start, j = nums.length - 1; i < j; i++, j--) {
swap(nums, i, j);
}
}
private void move(int[] nums, int start) {
int i = start + 1;
while(i < nums.length && nums[start] < nums[i]) {
i++;
}
swap(nums, start, i - 1);
}
public void nextPermutation(int[] nums) {
int i;
for(i = nums.length - 2; i >= 0; i--) {
if(nums[i] < nums[i + 1]) {
move(nums, i);
reverse(nums, i + 1);
break;
}
}
if(i < 0) reverse(nums, 0);
}
}
附1
假设我们现在有一个数组nums,长度至少为3,且存在1 <= i <= nums.length - 2
使得nums[i] < nums[i + 1]
,则我们所得到的下一个排列组合一定以nums[0], nums[1], ..., nums[i - 1]
开头。
证明:首先确定一点,保持开头nums[0], nums[1], ..., nums[i - 1]
不变,我们只交换nums[i], nums[i + 1], ..., nums[nums.length - 1]
的值,我们就一定可以得到一个排列组合nums2
,使得nums2 > nums
,至少交换nums[i]
和nums[i + 1]
就可以。这里我们使用反证法,假设下一个正确的排列组合nums3
不以nums[0], nums[1], ..., nums[i - 1]
开头,则一定有nums3 > nums2 > nums
或nums < nums2 < nums3
,不论是这两种情况下的哪一种,我们都可以发现比起nums3
来说,nums2
更接近nums
,也就是说,在nums2
存在的情况下,nums3
不可能是下一个排列组合,所以假设失败,所以下一个排列组合一定以nums[0], nums[1], nums[2], ..., nums[i - 1]
开头。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。