力扣-060 第k个排列

题目描述

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

“123”
“132”
“213”
“231”
“312”
“321”
给定 n 和 k,返回第 k 个排列。

说明:

给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。

样例

示例 1:

输入: n = 3, k = 3
输出: “213”
示例 2:

输入: n = 4, k = 9
输出: “2314”

分析

题目理解起来很简单,就是输出第k个排列,最简单的方法就是利用递归或者非递归的形式生成所有的排列,然后选择第k个,这样的时间复杂度为 O ( n ! ) O(n!) O(n!),时间有点长,那么,我们是否可以通过其他的方式直接获取到第k个排列呢?答案是有的。
我们先通过逐个确认排列里的每一个数来得到最终的排列。
首先我们设排列为 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an,我们来确认第一个 a 1 a_1 a1
根据排列的数量,我们可以很快的算出:
以1开始的排列有 ( n − 1 ) ! (n-1)! (n1)!个,以2开始的排列有 ( n − 1 ) ! (n-1)! (n1)!个, ⋯ \cdots ,以n开始的排列有 ( n − 1 ) ! (n-1)! (n1)!个。所以我们可以推断出当 ( i − 1 ) ( n − 1 ) ! < k ≤ i ( n − 1 ) ! (i-1)(n-1)!<k\leq i(n-1)! (i1)(n1)!<ki(n1)!时, a 1 = i a_1=i a1=i。我们可以通过公式计算出 a 1 = ⌊ k − 1 ( n − 1 ) ! ⌋ + 1 a_1=\lfloor\frac{k-1}{(n-1)!}\rfloor+1 a1=(n1)!k1+1,在完成这一步以后,我们就可以继续往下做了,此时待放入排列里的数就变成 [ 1 , n ] \ a 1 [1,n]\backslash a_1 [1,n]\a1,k变成了k’, k ′ = ( k − 1 ) m o d ( n − 1 ) ! + 1 k'=(k-1) mod(n-1)!+1 k=(k1)mod(n1)!+1。这样就变成从n-1个数里选择第k’个排列的子问题了,这样就可以进行递归或者循环下去。时间复杂度为 O ( n 2 ) O(n^2) O(n2)

代码

class Solution {
public:
    string getPermutation(int n, int k) {
        vector<int> factorial(n);
        factorial[0] = 1;
        for (int i = 1; i < n; ++i) {
            factorial[i] = factorial[i - 1] * i;
        }

        --k;
        string ans;
        vector<int> valid(n + 1, 1);
        for (int i = 1; i <= n; ++i) {
            int order = k / factorial[n - i] + 1;
            for (int j = 1; j <= n; ++j) {
                order -= valid[j];
                if (!order) {
                    ans += (j + '0');
                    valid[j] = 0;
                    break;
                }
            }
            k %= factorial[n - i];
        }   
        return ans;     
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值