描述:给定一个集合set={1,2,……,n}。按字典序递增的顺序求第K个排列组合。
输入:{1,2,3},K=2。
输出:{1,3,2}。
思路一:已知求下一个排列组合方法:next_permutation,先将集合元素按照递增排序,循环调用K-1次next_permutation,即可得到第K个排列。但是若K较大接近n!时,则会耗时过长。
class Solution{
public:
string k_thPermutaion(int A[],int n,int k)
{
sort(A,A+n);
string s;
for(int i=0;i<n;i++)
s+=A[i]+'0';
for(int i=0;i<k-1;i++)
next_permutation(s.begin(),s.end();
return s;
}
};
思路二:利用康托展开的逆序思想求解。
康托展开:X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0!,其中a[i]为当前元素在未使用元素中是排在第几个(从0开始)。康托展开用来确定按照字典序递增当前排列之前排列组合的数量。如:已知set={1,2,3}。当前排列为312,从左至右遍历排列元素。a[3]=2,因为当前元素3在未使用元素中{1,2,3}排第二个(从0开始)。a[2]=0,因为当前元素1在未使用元素中{1,2}排第零个(从0开始)。a[1]=0,因为当前元素2在未使用元素中{2}排第零个(从0开始)。因此X=2*(3-1)!+0*(2-1)!+0*(1-1)!=4,表示排列312之前还有四个字典序更小的排列,分别是123,132,213,231。因此我们可以通过康托展开的逆展开来确定当前第K个排列。
康托展开的逆展开:求解字典序递增的全排列中第K个排列。X=K-1,表示前面有K-1个字典序更小的排列。K-1=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0!。可使用辗转相除法分别确定a[n],……,a[1]。如:已知set={1,2,3,4}。求第10个排列(10<4!)。
10/(3!)=1 余 4 a[3]=1,即当前元素是未使用元素{1,2,3,4}中的第一个元素(从0开始)2,则第一个元素为2
4/(2!) =1 余 0 a[2]=1,即当前元素是未使用元素{1,3,4}中的第一个元素(从0开始)3,则第二个元素为3
0/(1!) =0 余 1 a[1]=0,即当前元素是未使用元素{1,4}中第0个元素(从0开始),则第三个元素为1
1/(0!) =0 余 0 a[0]=0,即当前元素是未使用元素{4}中第0个元素(从0开始),则第四个元素为4
则第10个排列为2314。时间复杂度为线性,空间复杂度为线性。
class Solution{
public:
string k_thPermutaion(int A[],int n,int k)
{
sort(A,A+n);
string s;
for(int i=0;i<n;i++)
s+=A[i]+'0';
string res;
k--;
for(int i=n-1;i=1;i--)
{
int tmp=k/fun(i);
res+=s[tmp];
s.erase(s.begin()+tmp);
k%=fun(i);
}
res+=s[0];
return res;
}
private:
int fun(int i)
{
if(i==0)return 1;
else
return i*fun(i-1);
}
};