下一个排列算法
题目描述:给你一个整数数组 nums ,找出 nums 的下一个排列。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列。
例如arr=[1,2,3],它的下一个排列为[1,3,2].
例如arr=[3,2,1],它是最大的排列,因此不存在
假设一个序列是升序序列:arr=[1,2,3,4],那么它是最小所有排列序列中最小的。
如果一个序列是降序序列,arr=[4,3,2,1],那么它是最大的序列,所以没有比之更小的了。
此外,由于寻找的是大于某个序列值的最小序列,所以尽可能的从右往左调整,例如:143652,如果调整左侧的值将4和6交换得到的值为[163452]一定大于交换3和6的值[146352]。
假设给定的数组arr=[1,4,8,6,5,2]。如下图所示:可以看出来从高到低的变化点在下标为2的位置处。
具体的思路如下:
1.从右向左找到第一个升序的子序列的位置,由于a[4]>a[5]跳过,a[3]>a[4]跳过,a[2]>a[3]跳过,a[1]<a[2],故该位置是下标为1的位置,对应元素为4.
2.从该位置开始,从右向左依次向遍历,如果遍历的数组元素大于升序位置处的元素,那么就交换两个元素的位置。交换之后的元素为[158642]。
需要注意的是,如果升序位置之后的序列小于当前位置的序列值,例如这里的2小于4,是不需要发生交换的。
3.将升序位置后的所有元素进行反转,反转之后的元素为[152468]
代码如下所示:
实现的方法有多种,自己习惯于使用for循环,满足条件退出这种的写法,其中left代表的是升序序列的位置,而right代表的是比arr[left]大的最小的下标位置。之后交换即可。
class Solution {
public:
void swap(int& left,int &right)
{
int tmp=right;
right=left;
left=tmp;
}
//采用指针的写法,注意这里的tmp是临时变量不能使用指针哈!
void swap(int *left,int *right)
{
int tmp=*right;
*right=*left;
*left=tmp;
}
void reverse(vector<int>& nums,int left,int right)
{
while(left<=right)
{
swap(&nums[left],&nums[right]);
left++;
right--;
}
}
void nextPermutation(vector<int>& nums) {
int i=nums.size()-2;
int left=-1;
for(int i=nums.size()-2;i>=0;i--)
{
if(nums[i]<nums[i+1])
{
left=i;
break;
}
}
if(left==-1)
{
reverse(nums,0,nums.size()-1);
return;
}
int right=nums.size()-1;
//注意这里是用来寻找right的,如果后面的数小于前面的数,是不能交换的。例如58432,如果交换5和2的话,结果会变得更小。
while(right>=(left+1)&&nums[right]<=nums[left])
right--;
//交换两个数
swap(nums[left],nums[right]);
reverse(nums,left+1,nums.size()-1);
}
};
上一个排列算法
同下一个排列算法很相似,该算法是用来寻找当前排列组合的上一个排列组合。
例如给定arr=[1,9,4,6,7],那么上一个排列组合就是[1,7,9,6,4]。
思路是类似的:由于需要的是小于当前序列的组合,因此如果序列是升序排列的话,它已经是最小的,所以需要寻找降序的下标位置。
由于这里仅仅交换了,如果需要寻找最大值的话,交换之后再逆序输出降序位置之后的元素就可以了。
需要注意以下这个点:
假设给定的arr=[3,1,1,3],如果没有考虑降序下标之后的元素相等的情况,就会有以下问题。因此如果元素相等的时候,需要向左继续遍历,需要添加这行代码哦!
while(j-1>i&&arr[j]==arr[j-1])
j--;
贴一个代码:
class Solution {
public:
vector<int> prevPermOpt1(vector<int>& arr) {
int i=arr.size()-2;
//i的取值从倒数第二个位置开始,如果是升序的话,直接跳过。
while(i>=0&&arr[i]<=arr[i+1])
i--;
if(i<0)
return arr;
if(i>=0)
{
int j=arr.size()-1;
while(j>i&&arr[i]<=arr[j])
j--;
//j-1的取值大于等于i+1
//必须要加,否则报错。
while(j-1>i&&arr[j]==arr[j-1])
j--;
swap(arr[i],arr[j]);
}
return arr;
}
};