【LeetCode】60.Permutation Sequence 输出第k个排列

一、概述

输入正整数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;
    }
};

注意先将阶乘计算出来保存在数组里可以节约很多时间。

三、总结

手动算一算每一位是怎么求出来的,就很容易发现规律。然后就迭代就好了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值