力扣31题-下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。
更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。
如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
一、问题描述
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。
更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。
如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
二、示例
三、提示
- 1 <= nums.length <= 100
- 0 <= nums[i] <= 100
四、问题分析
这道题题目看起来相当长,但其实要表达的意思很简单,比如给了你一个125,你需要找到这三个数字组成的比他大且相邻的下一组数,所以应该是152.
如果是321,没有比他大的,那么返回最小的排列,即123.
五、解法
public void nextPermutation(int[] nums) {
if(nums.length<2){
return ;
}
int i=nums.length-1;
while (nums[i]<=nums[i-1]){
i--;
if(i==0) {
//此时为最大数,需返回这个数组的逆序
int num = nums.length - 1;
int left = 0;
while (num > (nums.length - 1) / 2) {
int temp = nums[num];
nums[num] = nums[left];
nums[left] = temp;
num--;
left++;
}
//程序结束
return;
}
}
//此时遇到的是左边的数字小于右边的数字,
// 需要向右寻找在左边数字与右边数字范围之内的最小数字
int min=i;
for(int j=i-1;j<nums.length;j++){
if(nums[min]>nums[j]&&nums[j]>nums[i-1]){
min=j;
}
}
//先交换
int temp = nums[min];
nums[min] = nums[i - 1];
nums[i - 1] = temp;
//再对这个点后的数据进行从小到大的排序
for(int j=i;j<nums.length;j++){
for(int k=i;k<nums.length-1;k++) {
if (nums[k] > nums[k + 1]) {
//交换位置
int a = nums[k];
nums[k] = nums[k + 1];
nums[k + 1] = a;
}
}
}
return ;
}
六、代码分析
if(nums.length<2){
return ;
}
首先我们要考虑特殊情况:如果nums的长度为0或者1,此时没有下一个队列,我们直接return 即可。
int i=nums.length-1;
while (nums[i]<=nums[i-1]){
i--;
if(i==0) {
//此时为最大数,需返回这个数组的逆序
int num = nums.length - 1;
int left = 0;
while (num > (nums.length - 1) / 2) {
int temp = nums[num];
nums[num] = nums[left];
nums[left] = temp;
num--;
left++;
}
//程序结束
return;
}
}
接下来的ehile循环,则是为了找出数组从右向左第一个左边的数大于右边的数的索引,此时获得的索引就是i
例如这个数组,获取到的i就是1
如果在这个while循环中,一直找不到左边的数大于右边的数这种情况,则说明这个数组没有下一个排列,(比他大的)也是一种特殊情况
也就是这种情况,根据题目规定,它的下一个排列应当是它的逆序数组
因此,我们使用一个while循环对数组进行逆序操作。然后return。
//此时遇到的是左边的数字小于右边的数字,
// 需要向右寻找在左边数字与右边数字范围之内的最小数字
int min=i;
for(int j=i-1;j<nums.length;j++){
if(nums[min]>nums[j]&&nums[j]>nums[i-1]){
min=j;
}
}
此时已经找到我们需要的i索引,接下来需要找到从i到nums.length这段数组中的比nums[i-1]大,比nums[i]小的数,用min记录他的索引。
例如这两种情况,找到的min索引应当分别是2与1
if(min==i) {
// 如果要交换的两个数字相邻将这两个位置进行交换
int temp = nums[i];
nums[i] = nums[i - 1];
nums[i - 1] = temp;
}else {
//不相邻,先交换
int temp = nums[min];
nums[min] = nums[i - 1];
nums[i - 1] = temp;
}
然后进行我们将这两个数字进行交换
//再对这个点后的数据进行从小到大的排序
for(int j=i;j<nums.length;j++){
for(int k=i;k<nums.length-1;k++) {
if (nums[k] > nums[k + 1]) {
//交换位置
int a = nums[k];
nums[k] = nums[k + 1];
nums[k + 1] = a;
}
}
}
然后为了保证我们找到的是满足条件的最小队列
我们将i索引后的数据进行从小到大排序
然后return。
至此,所有情况已经满足。
七、运行结果
水平有限,希望能帮到大家!