Leetcode C++《热题 Hot 100-30》31.下一个排列
- 题目
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
在真实的面试中遇到过这道题?
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/next-permutation
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- 思路
- 复杂不是很可行思路
- 现在看到这些题目,我就想先暴力求解对吧,没有充分理解逆序对的概念
- 字典序的下一个更大的排列,那就是从左到右遍历,如果数字变小了,那就swap 143258,这个的下一个字典序应该是145238
- 如果一直递减, 证明就是最大的,nums进行反转输出
- 如果一直递增,从第二个元素开始找到后面略大于原来元素的值,如果相等继续找第三个元素
- 所以如果从左到右,有递减存在,比如x位置开始递减 nums[x] > nums[x+1]
- 那在x之后找到比x位置略大的数,放在x的位置,并将后面的数字从大到小排序
- 如果没有找到比x位置略大的数,那就找【x,】x之后比nums[x-1]略大的数放在x-1的位置
- 正确思路
- 我们可以逆序对来求解这个题目(找到“最小的”逆序对),空间复杂度为1,时间复杂度为n
- 对于一个排列,从后往前找到第一个开始减小的index
- 1 5 8 4 7 6 5 3 1, 找到的是4
- 从4的位置找最小的比4大的数,如果递增一直往后,如果递减判断是不是比4大,找到5
- 然后将4和5进行交换,变成了 1 5 8 5 [7 6 4 3 1]
- 我们可以看到最后是逆序的,然后要把5之后位置变成顺序的,做一个反转
- 如果已经是最大的了,比如 7 6 5 4 3 2 1 ,那从后到前就找不到第一个逆序对,最后做一个反转即可
- 代码
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int i;
bool flag = false;
for (i = nums.size()-1; i > 0; i--) {
if (nums[i-1] < nums[i]){
flag = true;
break;
}
}
if (!flag)
return reverseNum(nums, 0, nums.size()-1);
int swap_index = i;
for (int j = i; j < nums.size(); j++) {
if (nums[j] > nums[i-1]) {
if (nums[j] <= nums[swap_index]) //等号不能忘记,因为要尽可能和后面的值swap,得到一个较小的,比当前序列大的序列
swap_index = j;
if (j < (nums.size()-1) && nums[j+1] < nums[i-1])
break;
}
}
//cout << i-1 << endl;
//cout << swap_index << endl;
swap(nums[i-1], nums[swap_index]);
reverseNum(nums, i, nums.size()-1);
}
// [start,end]
void reverseNum(vector<int>& nums, int start, int end) {
for (int i = 0; i < (end-start)/2+1; i++)
swap(nums[start+i], nums[end-i]);
}
};