寻找排列数中的第k个数 Permutation Sequence

142 篇文章 20 订阅
51 篇文章 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):

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

Given n and k, return the kth permutation sequence.

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

思路:在《找出较大的下一个数字组合 Next Permutation》里,可以根据一种组合求得其相邻且较大的下一个组合。依据这样可以依次找到第k个数。但这样的效率太低了。况且两个问题题的条件是不一样的,之前那个问题的条件并不知道已知的组合是第几个数,而当前问题并不要求我们求出全部前k个数。

因此,不依次求到第k个数。可以根据k的值来直接构造。首先对于任一个数n,其组合个数都是知道的。分别是1!=1,2!=2,3!=6,4!=24,5!=120……这些组合数可以认为是某个位上的权值。

那么对于某一个n, k,比如n=4, k=17。首先知道n以下的组合数有6,2,1。那么17 = 2*6 + 2*2 + 1,

由于权值6的倍数是2,且有剩余,所以第4位(最高位)的值是从“1234”中选第3小的数,也就是3;

由于权值2的倍数是2,且有剩余,所以第3位的值是从“124”中选第3小的数,也就是4;

由于权值1的倍数是1,且没有剩余,所以第2位的值是从“12”中选第1小的数,也就是1;

最后剩下数,逆序追加到后面。

这个过程和人脑去寻找第k大数的过程很接近。

class Solution {
public:

//Remove the idx'th num from s
//取出第idx个数字
string removeidx(string s, int idx, char &c)
{
    c = s[idx-1];
    string s1 = s.substr(0, idx-1);
    string s2 = s.substr(idx);
    return s1 + s2;
}

    string getPermutation(int n, int k) {
        if(n > 9 || n<1)
            return "";
        string full = "123456789";
        int A[9] = {1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
        if(A[n-1] < k)
            return "";
        
        string s = full.substr(0, n); //1st permutation sequence
        if(k == 1) //第一个数,直接返回
            return s;
        if(k == A[n-1]) //最后一个数,逆转后直接返回
        {
            reverse(s.begin(), s.end());
            return s;
        }
        string result = "";
        int count;
        char c;
        for(int i=n-2;i>=0;i--)
        {
            count = 0;
            if(k < A[i]) //如果该位上没有个数,就选最小的一个数
            {
                s = removeidx(s, 1, c);
                result += c;
                continue;
            }
            while(k >= A[i]) //count求得该位上的个数
            {
                k -= A[i];
                count++;
            }
            
            if(k == 0) //全部减完,可以剩下的部分逆置来得到最大
            {
                    s = removeidx(s, count, c);
                    result += c;
                    reverse(s.begin(), s.end());
                    return result + s;
            }
            else //还没有减完,取得第count+1小的数来作该位
            {
                s = removeidx(s, count+1, c);
                result += c;
            }
        }
    }

};

上面的方法和《找1~n的自然数十进制形式中1的个数》所用的方法类似,http://blog.csdn.net/ojshilu/article/details/17089993

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值