题目描述:
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,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 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
c++代码:
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int l = nums.size();
if(l==1)return;
int index_left=-1,index_right=l;
for(int i=l-1;i>0;i--){
//存在降序子序列
if(nums[i-1]>=nums[i])continue;
//不存在降序子序列
else if(nums[i-1]<nums[i]){
index_left=i-1;
for(int j=l-1;j>index_left;j--){
if(nums[j]>nums[index_left]){
index_right=j;
break;
}
}
if(index_right == l){ //没有更大的下一序列
sort(nums.begin(),nums.end());
return;
}
swap(nums[index_left],nums[index_right]);
vector<int>::iterator it_left = nums.begin();
for(int j=0;j<=index_left;j++){
it_left = it_left + 1;
}
sort(it_left,nums.end());
return;
}
}
if(index_left == -1){ //没有更大的下一序列
sort(nums.begin(),nums.end());
return;
}
}
};
输入序列a,直接生成目标序列b,分类讨论:
【!注意降序概念:
一个降序子序列,说明该子序列已经是这个子序列的最大排序了,没有办法通过调整得到更大的子序列,若想要得到序列a的下一个更大的排序,只能考虑该子序列和其前一个数字构成的子序列。
例如序列a为(1,5,3,2,1),
则(3,2,1)是降序子序列,该子序列已经无法通过调整得到更大的排序了;
此时考虑(5,3,2,1)构成的子序列,该子序列可以通过调整得到下一个更大的子序列排序(3,1,2,5)
】
(1)若序列a是降序序列,则不可能有比a更大的排序方式。此时按照题目要求,应该输出序列a的最小排序。只需sort(a.begin(),a.end())
例如:a为(3,2,1)则b应为(1,2,3)
(2)若序列a不是降序序列,则从后向前查找降序子序列c,找到降序子序列前的一个数字n,对该数字n和该降序子序列c进行调整,得到下一个更大的子序列排序:在子序列c中找到比n大的最小数字m,交换n和m,此时再对新的子序列c从小到大排序,即可得到要求的序列b。
例如:a为(1,3,4,2,1),降序子序列为(4,2,1),由(3,4,2,1)构成的子序列需要调整,找到(4,2,1)中比3大的最小数3,交换4和3,再对(3,2,1)进行从小到大排序,最终得到序列b为(1,4,1,2,3)。
如果使用递归进行全排列,然后在所有排序结果中查找下一个序列,会超时,超时代码:
class Solution {
public:
//全排列
void Perm(vector<int> nums,int k,int m,vector<vector<int>> &res){
//如果要排序的数字只有一个(k=m时),只对nums[k]排序,则已经产生一个排序序列结果
if(k==m){
//记录当前排序
vector<int> this_res;
for(int i=0;i<=m;i++){
this_res.push_back(nums[i]);
}
//查找当前排序是否和之前记录的排序重复,若不重复则记录该排序,否则不记录
if(!count(res.begin(),res.end(),this_res)){
res.push_back(this_res);
}
}
else{
for(int i=k;i<=m;i++){
swap(nums[k],nums[i]);//依次把第k个数字换到最前面
Perm(nums,k+1,m,res);//已经把第k个数字换到最前面,只需对后面的数组递归地排序
swap(nums[k],nums[i]);//完成递归排序后,恢复原来的顺序,以便下一次换第k个数字到最前面
}
}
}
void nextPermutation(vector<int>& nums) {
vector<vector<int>> res;
vector<int> prenums = nums; //防止在递归过程中nums改变,要记录出入的初始nums
int l = nums.size();
if(l==1)return;
Perm(nums,0,l-1,res);
sort(res.begin(),res.end()); //对所有的排序结果,进行字典排序
for(int i=0;i<res.size();i++){ //在所有排序结果中查找nums的位置
if(prenums == res[i]){
if(i==res.size()-1)nums=res[0]; //若nums是字典序最大的排序,输出第一个(字典序最小的排序
else{ //若nums不是字典序最大的排序,输出它后面的下一个排序
nums=res[i+1];
break;
}
}
}
}
};
总结:
序列问题,首先考虑有特点的子序列,注意降序子序列和升序子序列。
找到一种调整方法,通过调整,直接求出要求的序列。使用列出全部排列结果的方法会超时。