关闭

Permutation Sequence

162人阅读 评论(0) 收藏 举报
分类:

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;
}
};

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:56340次
    • 积分:3124
    • 等级:
    • 排名:第11451名
    • 原创:255篇
    • 转载:1篇
    • 译文:0篇
    • 评论:1条
    最新评论