一天一大 lee(第k个排列)难度:中等-Day20200905

时间:2022-07-25
本文章向大家介绍一天一大 lee(第k个排列)难度:中等-Day20200905,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目:[1]

给出集合 [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. 示例 1
输入: n = 3, k = 3
输出: "213"
  1. 示例 2
输入: n = 4, k = 9
输出: "2314"

抛砖引玉

抛砖引玉

思路

从1到n转换成从n到1,共有n的阶乘种组合;

分析排列过程:

  • 1开始的排列(n-1)的阶乘种组合
  • 2开始的排列(n-1)的阶乘种组合 ....
  • n开始的排列(n-1)的阶乘种组合

结合排列的顺序和k的大小,此时可以确定第一个字符

第二个字符:

  • 此时已知两个字符剩余字符的组合种类数为(n-2)的阶乘,n-1的阶乘除以n-1

第三个字符:

  • 此时已知三个字符剩余字符的组合种类数为(n-3)的阶乘,n-2的阶乘除以n-2

...

知道第k中排列或者最终n-x等于1得到最终排列

数学

/**
 * @param {number} n
 * @param {number} k
 * @return {string}
 */
var getPermutation = function (n, k) {
  let _result = '',
      nums = [], // 存放待排列元素
      factorial = 1; 
  for (let i = 1; i <= n; i++) {
    nums.push(i); 
    factorial = factorial * i;
  }
  // 计数和索引差1
  k= k-1;

  while (nums.length > 0) {
    factorial = factorial / nums.length; // 确定一个元素后,待排列的组合种类减少为(n-x)的阶乘
    let index = parseInt(k / factorial,10);     // 当前选择的数字的索引
    _result += nums[index];               // 加上当前选的数字
    nums.splice(index, 1);               // nums删去选的这个数字
    k = k % factorial;                   // 更新 k,
  }
  return _result;
}

回溯法

上面的数学法并没有生成具体的组合,都是通过确定元素后能得到的排列组合数来推导出第k个排列

更直观的方法是,枚举每个位置上可能的元素然后记录其对应的种类数,直到枚举到第k

递归选择要拼接的元素:

  • 参数:已选择的元素数组
  • 终止:所有元素均被选择
var getPermutation = function (n, k) {
  let used = new Map(),
    factorial = 1;
  for (let i = 1; i <= n; i++) {
    factorial = factorial * i;
  }

  function helper(temp) {

    let len = temp.length;

    if (len == n) return temp.join('');

    factorial = factorial / (n - len); // 待排列的元素存在的组合数

    for (let i = 1; i <= n; i++) {
      if (used.has(i)) continue; // 递归时忽略已经排列的元素

      if (k > factorial) { // 如果k大于剩余的组合数,则说明如果此位置排列i则排列完成不能有k种组合
        k = k - factorial; 
        continue; 
      }
      temp.push(i);        // 满足此位置选择i包含大于等于k中组合
      used.set(i, true);
      return helper(temp); // 继续选择i后面的元素
    }
  };

  return helper([]);
}