31. 下一个排列
1.寻找规律,构造算法
-
因为题目要求我们不使用额外空间,因此我们不能构造出所有序列并且排序,需要根据当前序列直接推断出结果。
-
如果没有见过这样的题目,直接设计出优秀算法难度是比较大的,可以直接学一下题解中的优秀方法。如果不好理解力扣题解的描述,可以看看我下面的分析:
-
本题需要我们根据构造序列的规律,构造出符合条件的序列。我们假设自己没见过任何相关算法,试着分析一下这个问题:
- 假设给出的数字是123654,我们要怎么写出下一个比它大的序列?
- 不难发现后面三个数字654是完全倒序的,这就是6、5、4这三个数字可以组合成的最大序列。也说明123开头可以形成的最大序列也就是123654,123这个前缀已经走到头了。
- 寻找新前缀:我们为了找到仅仅比123大一点点的新前缀,就需要从后面的456里找出比3大的最小的数字——4,新前缀就是124。
- 交换位置:我们交换3、4的位置,新数字为124653;但我们注意到这并不符合要求,653并不是这三个数字构成的最小数。
- 排序:因此我们将后面三个数字排序,使它变为356,124356就是123654的下个数字。
-
我上面的分析假设了一个前缀、后缀的分界线,在123、654之间,我们接下来只需要找到方法确定其他情况的这个分界,整个算法就完备了。
-
这个“前缀”是从前往后遍历,最后一个nums[ind]<nums[ind+1]的位置,这个位置正是当前需要变大一点点的位置,也是增大幅度可以最小的位置。我们在这里只要从后往前找,第一个满足条件的位置即可。
-
注意,找到这个位置之后,ind之后的后半部分全部是倒序的,因此排序的部分,只要反转即可,就完成了从小到大的排序。
class Solution { public: void nextPermutation(vector<int>& nums) { int ind1=nums.size()-2,ind2=nums.size()-1; //寻找第一个满足nums[ind1]小、nums[ind1+1]大的位置 while(ind1>=0&&nums[ind1]>=nums[ind1+1]){ ind1--; } //如果知道最后都没有找到符合条件的ind1,说明已经形成了最大的数字,直接将数组整个反转。 if(ind1>=0){ while(ind2>=0&&nums[ind2]<=nums[ind1]){ ind2--; } swap(nums[ind1],nums[ind2]); } reverse(nums.begin()+ind1+1,nums.end()); } };