题目
题目地址:https://leetcode.cn/problems/next-permutation/
示例
方法1:大量的排序
思路作者:本人
题干的意思是:找出这个数组排序出的所有数中,刚好比当前数大的那个数
比如当前 nums = [1,2,3]。这个数是123,找出1,2,3这3个数字排序可能的所有数,排序后,比123大的那个数 也就是132
如果当前 nums = [3,2,1]。这就是1,2,3所有排序中最大的那个数,那么就返回1,2,3排序后所有数中最小的那个,也就是1,2,3 -> [1,2,3]
思路
主要的思路就是 看一个区间里面的数,这个区间刚开始最小,也就是最后两个数,一步一步变大。
定义一个指针fromIndex,先指向倒数第二个元素,区间就是【fromIndex,最后一个元素上】
如果 区间里面 不是从大到小的,那就给fromIndex后面的数从小到大排列,然后寻找在区间中的只比fromIndex大一点的数**,将**fromIndex和只比它大一点的数交换位置,返回结果。
否则 区间里面 是从大到小的,那就扩大范围,fromIndex--。
循环...
如果跳出了循环,说明区间已经是最大了,而且区间里面的数是从大到小,那我们就直接将它从小到大排列
举例说明
例如输入【1 2 3 4】
例如输入【1243】
代码(未优化)
class Solution {
public void nextPermutation(int[] nums) {
int fromIndex = nums.length - 2;
//判断是不是 从大到小排的,不是的话改成从大到小
while (fromIndex >= 0) {
if (isFromBigToSmall(nums,fromIndex)) {//如果是从大到小:扩大范围
fromIndex--;
} else {//如果不是从大到小:给fromIndex找大一点的,然后fromIndex 后面从小到达排序
//找升级???
Arrays.sort(nums,fromIndex+1,nums.length);//将fromIndex后面部分从小到大排序,前闭后开
for (int i=fromIndex+1;i<nums.length;i++){//找比它大的,交换位置
if (nums[fromIndex]<nums[i]){
int index = nums[i];
nums[i] = nums[fromIndex];
nums[fromIndex]= index;
break;
}
}
System.out.println("111"+Arrays.toString(nums));
return;
}
}
//跳出循环说明整个数组已经从大到小了,那就改成从小到大
Arrays.sort(nums);
System.out.println(Arrays.toString(nums));
}
//从fromIndex到最后 是否为从大到小
boolean isFromBigToSmall(int nums[],int fromIndex){
for (int i = fromIndex; i <= nums.length-1; i++) {
for (int j = i + 1; j <= nums.length-1; j++) {
if (nums[i] < nums[j]) {
return false;
}
}
}
return true;
}
}
效果
优化方法1
思路是一样的
方法1中,我们需要将部分数组从小到大排列的时候,直接用了java的sort方法,其实观察发现,当我们想要把后面的从小到大排列的时候,它们是从大到小排列的,所以可以用时间复杂度较低的翻转。
也就是用翻转代替 从小到大排列,效果是一样的,但时间复杂度更低。
例如:54321,它的下一个应该是12345,我们方法1是直接sort,这里可以优化成直接翻转。
例如:15432,当fromIndex在1时,方法1是将“5432”sort,其实可以优化为直接翻转。
优化后代码
class Solution {
public void nextPermutation(int[] nums) {
int fromIndex = nums.length - 2;
//判断是不是 从大到小排的,不是的话改成从大到小
while (fromIndex >= 0) {
if (isFromBigToSmall(nums,fromIndex)) {//如果是从大到小:扩大范围
fromIndex--;
continue;
} else {//如果不是从大到小: 给fromIndex找大一点的,然后fromIndex 后面从小到达排序
reverse(nums,fromIndex+1);//优化
// Arrays.sort(nums,fromIndex+1,nums.length);//将fromIndex后面部分从小到大排序,前闭后开
for (int i=fromIndex+1;i<nums.length;i++){//找比它大的,交换位置
if (nums[fromIndex]<nums[i]){
swap(nums,fromIndex,i);
break;
}
}
return;
}
}
//跳出循环说明整个数组已经从大到小了,那就改成从小到大
reverse(nums,0);//优化
return;
}
//从fromIndex到最后 是否为从大到小
boolean isFromBigToSmall(int nums[],int fromIndex){
for (int i = fromIndex; i <= nums.length-1; i++) {
for (int j = i + 1; j <= nums.length-1; j++) {
if (nums[i] < nums[j]) {
return false;
}
}
}
return true;
}
//交换
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
//反转数组
public void reverse(int[] nums, int start) {
int left = start, right = nums.length - 1;
while (left < right) {
swap(nums, left, right);
left++;
right--;
}
}
}
效果
可以看到优化完之后效率提升了不少。