【JS每日一算法】54.排列序列(逆康托展开)

178 阅读1分钟

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

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

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

给定nk,返回第k个排列。

提示:

  • 1 <= n <= 9
  • 1 <= k <= n!

示例:

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

题解:

更多JS版本题解点击链接关注该仓库👀

/**
 * @description: 逆康托展开   TC:O(n)  SC:O(n)
 * @author: JunLiangWang
 * @param {*} n   生成 n!种排列
 * @param {*} k   返回第 k 个排列
 * @return {*}
 */
function inverseCantorExpansion(n, k) {
    /**
     *  本方案使用逆康托展开的方法,试想当n=3时,产生如下序列:
     * 
     *   1."123"
     *   2."132"
     *   3."213"
     *   4."231"
     *   5."312"
     *   6."321"
     * 
     *   我们可以观察发现生成的序列的第一列为1,1,2,2,3,3
     *   从1到3两个一组,开始我们剩余未选择的字符为1,2,3,因此
     *   我们可以通过(k-1)/2得到未选择的字符的索引,假设k=1或2,此
     *   时计算出索引为0,第一个字符得出为1,剩余未选择的字符为2,3;
     *   假设k=3或4,此时计算出索引为1,第一个字符得出为2,剩余未选择
     *   的字符为1,3。上述公式中的2我们不难发现其实是n-1的阶乘,如果我们
     *   一开始就把k减去1,此处公式为k/(n-1的阶乘),设其结果为R1
     * 
     *    第二个字符呢?此时我们发现第二个字符的可以通过k%(n-1的阶乘)/(n-2的阶乘)
     *    得到未选择的字符的索引,比如k=1,计算得出索引为0,则得到未选择字符中的2,
     *    k=3,计算得出索引为1,则得到未选择字符中的1。
     * 
     *    第三个字符则以此类推公式为k%(n-1的阶乘)%(n-2的阶乘)/(n-3的阶乘)
     *  
     *    这种方式则被称为逆逆康托展开,我们定义一个字符串记录剩余未被选择的字符,
     *    然后利用迭代模拟逆康托展开过程,不断选择/删除剩余未被选择的字符,即可
     *    获得答案
     * 
     * 
     */
    // 记录剩余未被选择的字符,初值为[1,2,3......,n]
    let record = '',
        // 记录当前阶乘,初值为n-1的阶乘
        factorial = 1,
        // 记录输出数组
        outString = '';
    // 遍历生成record/factorial初值
    for (let i = 1; i <= n; i++) {
        factorial *= i;
        record += i
    }
    // k从1开始,需要将其减1,使之从0开始,方便选择record字符
    k--;
    // 迭代模拟逆康托展开过程
    while (n >= 1) {
        // 将当前阶乘除以n,以此不断获得n-1的阶乘,n-2的阶乘.....1的阶乘
        factorial = factorial / n;
        // 通过公式k/(n-i的阶乘),i从1到n-1,计算获得索引
        let index = Math.floor(k / factorial);
        // 从剩余未被选择的字符串中选择字符并记录
        outString += record[index];
        // 该字符已被选择,从剩余未被选择的字符串中删去
        record = record.replace(record[index], '')
        // 将k赋值为k%(n-i的阶乘),i从1到n-1,得到下一次k值
        k = k % factorial;
        n--;
    }
    // 返回结果
    return outString;
}

来源:力扣(LeetCode)