您的位置:首页 > 产品设计 > UI/UE

Permutation Sequence

2015-07-10 16:46 477 查看
The set [1,2,3,⋯,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order, We get the following sequence (ie, for n = 3):

“123”

“132”

“213”

“231”

“312”

“321”

Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

中文

给定n个数,求第k个排序。

{1,2,3,4,…,n}表示1,2,3,…,n的排列,如 {1,2,3} 按从小到大排列一共6个:123 132 213 231 312 321,代表数字 1 2 3 4 5 6,也就是把10进制数与一个排列对应起来,他们间的对应关系可由康托展开来找到。简单的说就是求一个排列数在所有排列中是第几小的。

德国数学家康托(应该不会重名吧)发现其实可以又更简单高效的算法来解决这个问题:例如我们求35412在{1,2,3,4,5}的生成的排列中是第几小的:

第一位是3,第一位比3小的排列数肯定小于35412,比3小的有1,2;共2个数,所以有2*4!;

第二位是5,同理,比5小的有1,2,3,4;因为3已经在前面出现了,所有还有3个比5小的,3*3!;

第三位是4,比4小的有1,2,3;3在前面出现了,还有2个比4小的数,2*2!;

第四位是1,没有比1小的数,所以是0*1!;

最后一位无论是几,比它小的数在前面肯定都出现了,所以有0*0!;

所以,比35412小的排列数共有:2*4!+3*3!+2*2!+0*1!+0*0!=70,35412是第71小的数。

class Solution {
public:
string getPermutation(int n, int k) {
int total = factorial(n);//获得总阶乘
//下标k从k-1开始,从0开始,就能与下面候选的字符串对应。0-n-1.当求n!个序列时,得到数字n,这样就不方便得到候选数字了。
string candidate = string("123456789").substr(0, n);//获得从0开始长度为n的字符串
string res(n,' ');//初始化字符串
for(int i = 0; i < n; i++)//依次计算排列的每个位
{
total /= (n-i);//第i个数后面的阶乘。
int index = (k-1) / total;//求得这一位的最前面的数。
res[i] = candidate[index];//将第i位保持起来。
candidate.erase(index, 1);//删除掉该数,删除从这个索引开始的1个字符
k -= index*total;//求得剩余的位数。k-前面数字确定的位数。
}
return res;
}
int factorial(int n)
{
int res = 1;
for(int i = 2; i <= n; i++)
res *= i;
return res;
}
};


这道题是让求出n个数字的第k个排列组合,由于其特殊性,我们不用将所有的排列组合的情况都求出来,然后返回其第k个,我们可以只求出第k个排列组合即可,那么难点就在于如何知道数字的排列顺序,可参见网友喜刷刷的博客,首先我们要知道当n = 3时,其排列组合共有3! = 6种,当n = 4时,其排列组合共有4! = 24种,我们就以n = 4, k = 17的情况来分析,所有排列组合情况如下:

1234

1243

1324

1342

1423

1432

2134

2143

2314

2341

2413

2431

3124

3142

3214

3241

3412 <— k = 17

3421

4123

4132

4213

4231

4312

4321

我们可以发现,每一位上1,2,3,4分别都出现了6次,当第一位上的数字确定了,后面三位上每个数字都出现了2次,当第二位也确定了,后面的数字都只出现了1次,当第三位确定了,那么第四位上的数字也只能出现一次,那么下面我们来看k = 17这种情况的每位数字如何确定,由于k = 17是转化为数组下标为16:

最高位可取1,2,3,4中的一个,每个数字出现3!= 6次,所以k = 16的第一位数字的下标为16 / 6 = 2,即3被取出

第二位此时从1,2,4中取一个,k = 16是此时的k’ = 16 % (3!) = 4,而剩下的每个数字出现2!= 2次,所以第二数字的下标为4 / 2 = 2,即4被取出

第三位此时从1,2中去一个,k’ = 4是此时的k” = 4 % (2!) = 0,而剩下的每个数字出现1!= 1次,所以第三个数字的下标为 0 / 1 = 0,即1被取出

第四位是从2中取一个,k” = 0是此时的k”’ = 0 % (1!) = 0,而剩下的每个数字出现0!= 1次,所以第四个数字的下标为0 / 1= 0,即2被取出

那么我们就可以找出规律了

a1 = k / (n - 1)!

k1 = k

a2 = k1 / (n - 2)!

k2 = k1 % (n - 2)!



an-1 = kn-2 / 1!

kn-1 = kn-2 / 1!

an = kn-1 / 0!

kn = kn-1 % 0!

代码如下:

class Solution {

public:

string getPermutation(int n, int k) {

string res;

string num = “123456789”;

vector f(n, 1);

for (int i = 1; i < n; ++i) f[i] = f[i - 1] * i;

–k;

for (int i = n; i >= 1; –i) {

int j = k / f[i - 1];

k %= f[i - 1];

res.push_back(num[j]);

num.erase(j, 1);

}

return res;

}

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: