题目:
给出一个数组,默认为int型,现需求出数组内元素按照字典序排列的下一个排列,说白了就是说找出数组内的元素可以组成一个序列,这个序列刚刚好比原序列大,要求inplace调整。如果原排列就是最大的排列,那么返回最小的排列,这样听起来太绕了,看下例子应该就懂了。
举例:
1,2,3 ----> 1,3,2原因是1,2,3可以组成的顺序有123,132,213,231,312,321,其中刚好比123大的就是132
3,2,1 ----> 1,2,3因为321是1,2,3三个数的所有排列中最大的那个,所以应将数组调整为123,最小的排列。
按照题目的要求,以上的排列的大小的比较标准都应该按照字典序,比如1,10,110,2,3这样的,但是由于在submit之前没有想到这个事情,直接将大小的比较标准按数字的大小进行,之后才想起来,但是已经通过了,所有也就没有再管,谁知道oj判断条件和它本身的题目的意思是不是相同的呢,或者是我理解错题目中的lexicographically next greater permutation意思了。
思路:
看一个例子,14765321,仅仅比它大的排列是15123467,我们做的操作是将第二位的4和第五位的5调换了一下位置,然后将后六位的顺序逆置了以下。再看一个例子,17654312,仅仅比它大的排列是17654321,我们做的操作是将倒数第二位的2和倒数第一位的1调换了一下位置。7654321,不存在仅仅比它大的例子,所以我们的结果应该是将这个数逆置为1234567。从上面的例子加上自己测试的其他例子,可以总结出如何得到一个仅仅比当前排列大的排列的方法:首先从后往前找,找到第一组满足nums[i+1]>nums[i]大的i,然后在i-nums.length之间找到一个数nums[j]满足j>i,且nums[j]>nums[i],nums[j+1]<nums[i],然后调换nums[i]和nums[j],然后将i-nums.length之间的数逆置即可。如果j不存在,那么不做调换操作,如果i不存在,那么直接做逆置操作。
代码:
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
void nextPermutation(vector<int>& nums)
{
int length = nums.size();
if(length == 1 || length == 0) return;
int i,j,temp;
for(i = length - 2;i >= 0;i--)
if(nums[i + 1] > nums[i]) break;
if(i >= 0)
{
for(j = length - 1;j > i;j--)
if(nums[j]>nums[i]) break;
temp = nums[i];nums[i] = nums[j];nums[j] = temp;
}
j = length - 1;
i++;
while(i < j)
{
temp = nums[i];nums[i] = nums[j];nums[j] = temp;
i++;j--;
}
}
int main()
{
vector<int> nums;
nums.push_back(1);
nums.push_back(5);
nums.push_back(8);
nums.push_back(4);
nums.push_back(7);
nums.push_back(6);
nums.push_back(5);
nums.push_back(3);
nums.push_back(1);
nextPermutation(nums);
cout<<nums[0]<<endl;
cout<<nums[1]<<endl;
cout<<nums[2]<<endl;
cout<<nums[3]<<endl;
cout<<nums[4]<<endl;
cout<<nums[5]<<endl;
cout<<nums[6]<<endl;
cout<<nums[7]<<endl;
cout<<nums[8]<<endl;
return 0;
}
时间复杂度:
具体的运行时间和测试数据有很大的关系,这里只分析一个最坏情况,最坏情况下,找i需要pass一次数组,找j需要pass一次数组,逆置i-nums.length部分的序列需要经过nums.length/2的长度,设n = nums.length,所以最坏情况下时间复杂度为O(2.5n)。
空间复杂度:
O(1)。