leetcode 60.第k个排列

leetcode 60.第k个排列

题目描述

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

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

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

给定 n 和 k,返回第 k 个排列。

说明:

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

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

示例 1:

输入: n = 3, k = 3
输出: "213"

示例 2:

输入: n = 4, k = 9
输出: "2314"

解题思路

本题可以选择使用回溯法解题,但是时间复杂度太高,使用数学的思维去解题,下面分享一下解题思路。

给定一个数字n和一个数字k,那么如果可以直接求出所有的组合,那么直接就可以使用下标索引的方式直接找到第k个排列,那么又是是如何找到所有的排序的。即首先固定第一位,有n种可能;固定第二位有n-1中可能;…;第n位有1种可能。那么说了那么多如何直接计算第k个呢?首先,如果固定了第一位的数字,那么剩下的没有排列的n-1位,可以有(n-1)!种排列方式,如果在固定第一位的基础上固定第二位,那么剩下的未排列的n-2位就有(n-2)!种排列方式。通过这样的计算,给定一个k之后,就可以确定每一个位对应什么数字。这样说可能不是很明白,那么以题中的示例2举例,直接计算出第k个排列

例子: n = 4, k = 9

  • 首先计算第一位,第一位可以选择1、2、3、4四个数,选定以后每一个数字对应的是(n-1)!种排列,由于是按照顺序排列的,如果第一位选择1,那么排完1就可以产生6个排列,即第六个就是最有一个以1作为开头的排序,同理排完2,就会产生12个排列,那么给定一个数字k,就可以确定第一位是对应的数字了。例题中给的k=9,那么 9/6 = 1.5,这里如果不是整除,直接向上取整。即9/6=2;那么第一位对应的数字就是2
  • 然后是确定第二位,由于数字2已经使用了,所有要把它删除,第二位可以使用的数字是1、3、4。这时的k也是需要改变的,确定了第一个数字以后,可以知道目标排列就在以数字2开头的排列中,由于是顺序排列的所以,需要减去数字1开头的排序,k = k-((n-1)!) * num,这里的num是指的第几个数字,这里是数字1,那么如果我们的目标是3开头的排序,就需要减去1、2开头的所有排列,即 k = k-((n-1)!) * 2. 所有k = 9 - 1*((4-1)!) = 3,此时n=3,第二位数字是3/((3-1)!) = 2,这时未排序的数组中只有1、3、4,所以第二个数字是3
  • 第三个数字参考上面两步,先计算现在的k = 3 - 1*((3-1)!) = 1, 此时的n = 2 ,第三个数字的下标是 1/((2-1)!) = 1,此时数组里面的数字是1、4,所以第三个数字是1
  • 第四个数字,其实已经拍完了,就剩一个数字4了。同样的我们继续按照上面的方式计算一下,k = 1- 0*((2-1)!),此时的n=1; 我们定义 数字0的阶乘等于1,所以第四个数字为 1/((1-1)!) = 1,此时数组里面只有数字4
  • 所以第9个排列是 2314
class Solution {
public:
    string getPermutation(int n, int k) {
        string str = "123456789";
        string res = "";

        while(n){
            int num = 0; // 用于表示计算对应位,应该选择哪一个数字
            if(k%factorial(n-1)){
                num = k/factorial(n-1) +1; 
            }
            else{
                num = k/factorial(n-1);
            }
            
            res += str[num-1];  // 添加到字符串
            str.erase(str.begin()+num-1); // 使用过的字符串从待定的字符串中删除
            
            k -= factorial(n-1)*(num-1); // 计算新的k
            n--;                         // 确定了一位数字以后,n的数量要减少1
        }

        return res;
    }

    // 阶乘
    int factorial(int n){
        if(n == 0){
            return 1;
        }
        else{
            return n*factorial(n-1);
        }
    }
};

欢迎大家关注我的个人公众号,同样的也是和该博客账号一样,专注分享技术问题,我们一起学习进步
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值