题干:
https://leetcode.cn/problems/next-permutation/
还能说什么呢?
//关于next_permutation(),请查阅资料
class Solution {
public:
void nextPermutation(vector<int>& nums) {
next_permutation(nums.begin(), nums.end());
}
};
自己写的算法:
思考:在拥有了一个排列之后我们应该如何得到下一个排列呢?
1、我们应该从后往前看。
2、如果我们只看最后两个数的话,很简单。
----如果这两个数是升序,那么这两个数就是最小排列,整个数组下一个排列就是前面数不变,最后两个数调换。
----如果这两个数是降序,那么这两个数是最大排列,那么我们就要更改(从后往前的)三个数(假设第三个数<倒数第二个数,如果后三个数都是降序我们就要借助倒数第四个数,以此类推)。我们要将倒数第三个数变成字典序中一个更大的数,算法就是我们要从后面两个数中选择大于倒数第三个数的数(这个数要尽可能小),然后将后面两个数改成升序(最小排列)。
总而言之:
先看最后两个数,如果是升序就改为降序。如果后两个数是降序,再加入倒数第三个数,如果还是降序,再加入倒数第四个数…直到破坏了后n个数的降序结构(n个数是降序,n+1个数不是降序)。此时我们要从后n个数中寻找大于倒数第n+1个数的最小的那个数,将它与第n+1个数调换,然后把后n个数变为升序(最小排列)。其实我们也可以把倒数第一个数当作降序,对应n == 1的情况,这样就统一了。
算法伪代码如下:
看后n个数(n == 1,2,3…n<数组size(),n == 1视作降序),如果第n+1个数破坏了降序结构,我们要从后n个数中寻找大于倒数第n+1个数的最小的那个数,将它与第n+1个数调换,然后把后n个数变为升序(最小排列)。
降序区间不太准确,应该是非升序区间,因为有相等的元素。
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int left{ -1 };//破坏降序区间的位置
int right{ static_cast<int>(nums.size() - 1) };// 闭区间 [right,nums.size()-1] 为降序区间
//保证 right-1 不越界,寻找降序区间 (其实是非升序区间)
while (right > 0 && nums[right-1] >= nums[right]) {
right--;
}
if (right == 0) { }//整个数组都是降序的
else {
//把right == 0作为另一个分支,是因为这个分支可以避免left越界
left = right - 1;
//在降序区间寻找大于nums[left]的最小的那个,rightCopy不会越界,因为right!=0,最多rightCopy遍历完整个[right,nums.size()-1]区间,就肯定能找到
int rightCopy{ static_cast<int>(nums.size() - 1) };
while (nums[rightCopy] <= nums[left]) {rightCopy--; }
swap(nums[left], nums[rightCopy]);//此时[right,nums.size()-1]是仍然是降序的,请思考原因
}
//我们要把降序区间变为升序,前面已经说了[right,nums.size()-1]是仍然是降序的,所以可以调用reverse()。
//这一步在if-else的两个分支中都要做,所以放在了这里
reverse(nums.begin() + right, nums.end());
//sort(nums.begin() + right, nums.end());也可以,但是感觉没有reverse()快
}
};