每天一道LeetCode-----找到第k个排列

Permutation Sequence

原题链接Permutation Sequence
这里写图片描述
给定n和k,求[1,2,3,...,n]这个序列的全排列中第k个排列


可以调用k次next_permutation获取结果,但是next_permutation内部实现比较慢。
首先考虑能不能确定第k个排列是以哪个数字开头的呢,以[1,2,3,4]的全排列为例,找第14个排列

  • 以1开头的排列总共有3!个,原因是第一个位置是1,剩下3个位置可以随便排列,有6个
  • 以2开头的排列总共有3!个,原因是第一个位置是2,剩下3个位置可以随便排列,有6个
  • 此时已经有12个排列
  • 所以剩下的两个排列即第14个排列一定在以3开头的排列中

用这种方式继续缩减数量,以3开头的排列中最小的为[3,1,2,4],3已经固定,那么就找[1,2,4]的全排列的第2个排列,就是整个排列的第14个排列

  • 以1开头的排列共有2!个,原因是第二个位置是1,剩下2个位置可以随便排列,有2个
  • 此时已经有两个排列,可以确定结果一定在以[3,1]开头的排列中,即[3,1,2,4][3,1,4,2]

继续缩减数量,以[3,1]开头的排列中最下的为[3,1,2,4][3,1]已经固定,那么就找[2,4]的全排列的第2个排列,就是[1,2,4]的全排列的第2个排列,也就是整个排列的第14个排列

  • 以2开头的排列共有1!个,原因是第三个位置是2,剩下一个位置给4,有1个
  • 以4开头的排列共有1!个,原因是第三个位置是4,剩下一个位置给12,有1个
  • 此时已经有两个排列,可以确定结果是以4开头的排列,即[4,2],所以结果为[3,1,4,2]

所以,可以每次确定一个大范围,在大范围的基础上进一步缩小范围,直到最后只有一个数字为止。遍历n遍即可。

假设某次需要找到第k个排列(k从1开始),以第i个位置开头(i从1开始),上述过程可以表示为
要找的排列的开头是所剩数字中的第k / (n-i)!个 (整除)或第k / (n-i)! + 1个(非整除)数字,(从1开始)

原因
上述第一步,序列为[1,2,3,4],k为14,i为1,(n-i)!为6,k / (n-i)!为2,此时因为要找第14个排列(从1开始),以1,2开头的各占6个,所以在以3开头的排列中找,所以应该是k / (n-i)! + 1(非整除),即第3个数字,从1开始,为3
上述第二步,序列为[1,2,3],确定以3开头后,k为2,i为2,(n-i)!为2,k / (n-i)!为1,此时因为要找第2个排列,以1开头的就有2个,所以在以1开头的排列中找,所以应该是k / (n-i)! (整除),即第1个数字,从1开始,为1

上面需要根据情况讨论的原因是位置索引都是从1开始的,如果索引从0开始,那么就不会有这么多问题,此时k为14,即要找第13个排列(从0开始)
上述第一步,序列为[1,2,3,4],k为13,i为1,(n-i)!为6,k / (n-i)!为2,k / (n-i)!为2,即第2个数字(从0开始),为3
上述第二步,序列为[1,2,3],确定以3开头后,k为1,i为2,(n-i)!为2,k / (n-i)!为0,即第0个数字,为1

代码如下

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

        vector<int> nums;
        for(int i = 1; i <= n; ++i)
            nums.emplace_back(i);

        string res("");
        for(int i = 1; i <= n; ++i)
        {
            /* 找开始点 */
            int index = k / factorial[n - i];
            /* 如果非整除,加一 */
            if(k % factorial[n - i] != 0)
                ++index;
            res += (nums[index - 1] + '0');
            nums.erase(nums.begin() + index - 1);
            k = k - ((index - 1) * factorial[n - i]);
        }

        return res;
    }
};
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值