31. 下一个排列
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
解题思路: 此题属于找规律题,我们可以把一个序列的全排列写出来,然后对比找规律,比如序列1,2,3,4,5,全排列如下:
1 2 3 4 5
1 2 3 5 4
1 2 4 3 5
1 2 4 5 3
1 2 5 3 4
1 2 5 4 3
1 3 2 4 5
1 3 2 5 4
…
我们观察1 2 5 4 3->1 3 2 4 5变化过程,发现从右往左遍历过程中,2 5破坏了递增趋势,然后对右边序列从右往左遍历找到第一个大于2的元素3,然后将2和3交换位置,右边剩下的序列5 4 2按照升序排列得到2 4 5,最后得到的序列正好是下一个排列。
下面我们探讨一下,假设拿到这个题,根本不知道上面的解法,或者说根本不知道从找规律的角度去解题,那我们能不能尝试着从题目的本质去找寻解题的思路呢?我们分析这题,此题要求的是求下一个排列,我们之前做过求全排列的题,一个最直观的想法是,我们可以先把序列的全排列求出来,然后在这个全排列中找到当前的这个序列,然后这个序列位置的下一个序列即为下一个排列,当然,这个思路是挺直接的,但是代价是空间复杂度和时间复杂度都高,面试的时候用这种解法肯定很失分,OK那我们换个角度想想,排列的原则是一开始尽量将较大的值往后排,当较大的值在后半序列排完了后,在把它与前面恰好小于它的数交换,然后后边序列全部升序,就能得到它的下一个排序,那这个思路是不是有点像我们上面的解题思路,那么接下来就是如何确定这个需要调整的后半序列,自然到这里我们就会试想着把序列变化过程写出来,然后会按照破坏递增趋势为分界点划分前序列(不用调整的)和后序列(需要调整的)。
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int n = nums.size();
for (int i = n - 1; i >= 1; --i) {
if (nums[i - 1] < nums[i]) {
int j = n - 1;
while (nums[j] <= nums[i - 1]) --j;
swap(nums[j], nums[i - 1]);
sort(nums.begin() + i, nums.end());
return;
}
}
reverse(nums.begin(), nums.end());
}
};