LeetCode 讨论区中有一种解法看上去比较清晰,但是一开始不是太容易理解,分享一下自己的理解.
完成这道题分成3部分:
1)找到应该增大的位置i
2)将nums[i] 替换成一个比较大的数 nums[j]
3)将[i+1,末尾]的数都翻转一次
感性的认识:next permutation 是要求 比当前这个数 稍微大一点点点的 那个数 ,那我们肯定是让 后面的位 增大, 比如 123456 ,
肯定是让123456 中的"5" 增大 , 这个5的位置是怎么来的呢?
理性的推理:我们从上面的例子中可以推理出,我们要增大的位置 一定是 相对靠后的 , 那么就可以 从后往前开始遍历 ,
判断 nums[i] < nums[i+1] 是否成立 , 如果成立的话 , 说明nums[i] 这个位上的数 就可以增大 . 如果不成立 , 那么 i-- 即可
int i ; //i是当前考察的位置 , 与 i+1位置上的数进行比较
for(i = nums.size() ; i >= 0 ; i-- )
{
if(nums[i] < nums[i+1])
break;
}
这样我们就记录了 从后往前数 第一个可以被增大的位置 , 而且可以推断出来的是 :范围[i+1,nums.size()-1] 区间的 数 一定是单调递减的,举个例子: 123459876中 5是第一个从后往前找到的 非递减的位置, 其后面的第6个位置到第9个位置都是单调递减的.
值得注意的点是: i=-1的情况,意味着 整个序列都是单调递减的,那么我们就可以直接进入第3步 , 将987654321 翻转成 123456789 当i >= 0 的情况 ,我们要进入第2步 现在我们完成了第1步
第2步中我们需要把nums[i] 替换成比它本身大的那个数 , 这个数 只能从[i+1,nums.size()-1]的范围中寻找(因为[0,i-1]范围内的数是不能动的),又因为[i+1,nums.size()-1]范围内的数是单调递减的 , 所以,我们可以从 后面开始寻找 第一个 大于 nums[i]的位置 j ,然后swap(nums[i] , nums[j]) ,交换后,[i+1,nums.size()-1]范围的数 仍然是单调递减的 .
举两个例子:123459876可以知道 5 和 6进行交换 123469875
12345987654321可以知道5也和6进行交换 , 12346987554321
这样我们完成了第2步.
对i这个位置上的数进行增大后,我们要让[i+1,nums.size()-1]范围内的数 从小到大排列 , 而现在是 递减的(非严格递减) ,所以最后添加一句reverse(nums.begin()+i+1,nums.end()) ;
class Solution {
public:
void nextPermutation(vector& nums) {
int i ;
for(i = nums.size()-2 ; i >= 0 ;i--)
{
if(nums[i] < nums[i+1])
break;
}
if(i == -1)
{
reverse(nums.begin() , nums.end());
return ;
}
int j ;
for(j = nums.size() -1 ; j > i ; j-- )
{
if(nums[j] > nums[i])
break ;
}
swap(nums[i],nums[j]);
reverse(nums.begin() + i + 1 , nums.end());
return ;
}
};