一、概述
输入正整数n和k,输出包含1~n共n个整数组成的排列中的第k个。
比如说n=3,则排列有123,132,213,231,312,321六个。k=3则输出213。
我使用递归求解,很容易看懂,时间复杂度很好,空间复杂度还可以。
二、分析
n=3我们不容易看出来。我们以n=4为例:
1234
1243
1324
1342
1423
1432
2134
2143
2314
2341
2413
2431
......
我们通过观察可以发现,排列是按由小到大的顺序确定的。一共就n个数,设置我们的“原料为”1234。
共有n!=24个排列,其中,前(n-1)!=6个第一位为1,(n-1)!+1到(n-1)!+(n-1)!个第一位为2;以此类推。
也就是说,第一位我们可以通过k/(n-1)!进行向上取整求出,取出“原料”中的第ceil(k/(n-1)!)个,令loc1=ceil(k/(n-1)!)。原料变为134。
那么第二位有什么规律呢?
将第一位为2的单拿出来:
2134
2143
2314
2341
2413
2431
同样的,共有(n-1)!个排列,其中,前(n-2)!=2个第二位为1,(n-2)!+1到(n-2)!+(n-2)!个第二位为3;以此类推。
第二位怎么求呢?不容易直接看出来。可以这样看:首先,我们要求所有排列的第k个,当我们将第一位a确定后,我们也就砍掉了第一位小于a的所有排列,即砍掉了(a-1)*(n-1)!个排列,同时我们得到了一个长度为(n-1)!的新排列。那么,我们就要求出新的排列中,我们要第几个——第k-(loc1-1)*(n-1)!个,这就是新的k,如何由k确定要“原料”中的第几个呢?ceil(k/(n-1)!)啊,具体到本例就是ceil((k-(a-1)*(n-1)!)/(n-2)!),设置其为,也就是ceil((9-(2-1)*(4-1)!)/(4-2)!)=2,loc2=2。也就是3,原料变为14。
将第一位为2和第二位为3的拿出来,新排列有(n-2)!个排列:
2314
2341
前(n-3)!=1个第三位为1,然后是4。
第三位怎么求呢?类似第二位,我们要确定新排列中我们要第几个:第k-(loc1-1)*(n-1)!-(loc2-1)*(n-2)!=9-(2-1)*6-(2-1)*2=1,这就是新的k。那么loc=ceil(k/(n-3)!)=1/1!=1。取出原料的第1个,原料剩余4。
把4放在最后,得到结果。
用文字说明又复杂又难以理解,写成式子就很容易看:
代码如下:
class Solution {
string res;
int n_fact[11]={0,1,2,6,24,120,720,5040,40320,362880};
void generate(string s,int k,int n)
{
if(s.size()==1)
{
res+=(s[0]);
return;
}
else
{
int loc=ceil(k*1.0/n_fact[n-1]);
res+=(s[loc-1]);
s.erase(s.begin()+loc-1);
generate(s,k-(loc-1)*n_fact[n-1],n-1);
}
}
public:
string getPermutation(int n, int k) {
string s;
for(int i=1;i<=n;i++)
s+=(i+'0');
generate(s,k,n);
return res;
}
};
注意先将阶乘计算出来保存在数组里可以节约很多时间。
三、总结
手动算一算每一位是怎么求出来的,就很容易发现规律。然后就迭代就好了。