我们要做的就是让左边一个较小的数与右边一个较大的数进行交换,而且,要让 ”较小的数尽可能靠近右边,而较大的数尽可能的小“
而且再交换之后,要让i到n-1上的数字从小到大,这一点很重要,只有这样才能保证变大的幅度最小
越靠右的话改变的位置越小,十位的变动肯定比百位,千位引起的变化小的多
改变的时候,肯定是想又能变大,又尽可能的变大的比较少
有了这些基础我们就可以来写这道题了:
我们先从后向前,去找第一个出现nums[ i-1 ] < nums[ i ] 的情况,那么在i到n-1的区间上,肯定是降序的,因为都不满足前面的条件嘛,所以说我们可以从后面的区间 i 到 n-1上 从后向前去找到到第一个元素j,满足nums[ i-1 ] < nums[ j ],然后交换这两个数的值
交换之后这个这个区间也还是降序的,我们只需要翻转reverse一下就能得到升序,这个复杂度也是O(n),如果用sort的话复杂度是O(nlogn)。外面一次遍历是O(n),内部一次翻转,这两个是并列的,相当于最多进行两次遍历,也就是2 * O(n),也就是说时间复杂度能够达到O(n),实在是妙哉!
//两次扫描
void nextPermutation(vector<int>& nums) {
int n=nums.size();
for(int i=n-1;i>0;--i){
if(nums[i-1]<nums[i]){//找到第一个i-1 < i的位置
//i到n-1上肯定是降序
//从i到n-1上从后向前找到第一个满足nums[i-1]<nums[j]的数,这个数就是这里面最小的,而且大于nums[i-1]的数,交换以后这个区间还是降序的,reverse之后就能满足升序
for(int j=n-1;j>=i;--j){
if(nums[i-1]<nums[j]){
int tmp=nums[i-1];//交换
nums[i-1]=nums[j];
nums[j]=tmp;
//翻转 i 到 n-1 这个区间,就能让这个区间升序
//sort(nums.begin()+i,nums.end())
reverse(nums.begin()+i,nums.end());
return;
}
}
}
}
//如果没有返回则说明需要从到到尾翻转
reverse(nums.begin(),nums.end());
}
当我们用sort的时候:
而我们用了reverse的话:
可以看到,reverse是真的秒啊