Day18[26/3/18]T31下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
arr = [1,2,3],以下这些都可以视作arr的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1]。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,
arr = [1,2,3]的下一个排列是[1,3,2]。 - 类似地,
arr = [2,3,1]的下一个排列是[3,1,2]。 - 而
arr = [3,2,1]的下一个排列是[1,2,3],因为[3,2,1]不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须原地修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]
提示:
1 <= nums.length <= 1000 <= nums[i] <= 100
解题思路
首先想到,下一个排列和原排列相比,结构应该是:前 n 个数字都是相同的。
然后对于第 n+1 个数字是,下一个排列上的数字比原排列大一丢丢而已。
此外,第 n+1 位之后的数字应当是升序排列的。
例如说:9874543 的下一个排列应当是 9875344
但是继续观察可以发现,原排列的第 n+1 位之后肯定是降序的,然后下一个排列的 n+1 位之后肯定是升序的,并且第 n+1 位置上要和后面出现过的,仅比第 n+1 位数字大的最小数字交换。
所以算法步骤为:
先从后往前检查是不是全是满足前一位数字大于后一位,如果是,那么恭喜,这就是最大值,下一个排列是最小值,直接 sort。
如果发现第 n 位上的数字大于前一个数字,那么说明前一个数字小了,可以交换成大一点的,那么这个大一点的数字又恰好可以从后往前搜索,搜到的第一个大一点的数字就交换就行。最妙的是,交换完成之后,第 n 位后面的数字依旧是降序的,要改成升序只需要局部 sort 就可以了。
Code
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution
{
public:
void nextPermutation(vector<int> &nums)
{
for (int i = nums.size() - 1; i > 0; i--)
{
// 找到升序就需要处理
if (nums[i] > nums[i - 1])
{
int j = nums.size() - 1;
for (; j > i - 1; j--) // 这里可以二分优化
{
if (nums[j] > nums[i - 1])
{
break;
}
}
swap(nums[i - 1], nums[j]);
reverse(nums.begin() + i, nums.end());
return;
}
}
// 肯定是最大了,要排序成最小
sort(nums.begin(), nums.end());
}
};
auto main() -> int
{
vector<int> nums{1, 2, 3, 4, 5, 4, 3};
Solution sol;
cout << "原始: ";
for (const auto &num : nums)
cout << num << ",";
cout << endl
<< "修改: ";
sol.nextPermutation(nums);
for (const auto &num : nums)
cout
<< num << ",";
cout << endl;
}