Last Commit: 2024-01-06 17:50:27

views:

440. K-th Smallest in Lexicographical Order

source: https://leetcode.com/problems/k-th-smallest-in-lexicographical-order/

Question

Given two integers n and k, return the kth lexicographically smallest integer in the range [1, n].

暴力法

这道题比较自然的反应是生成数组,然后排序,再通过数组索引选择。

var findKthNumber = function(n, k) {
    return Array(n).fill(0).map((_, i) => `${i + 1}`).sort()[k - 1];
};

这样子的时间复杂度在O(nlogn), 空间复杂度在O(logn). 这道题n的数字会很大,会导致oom.

字典树

这题的核心在于,字典树就是一个十叉树的结构,而对于一个树形的结构,因为已知总节点数,他的某个子节点数是可计算的。

通过这个原理:

  • 如果此节点下的子节点数小于k,说明k在子节点内,继续以第一个子节点为节点计算
  • 如果此节点下的子节点数大于k,说明k不在子节点内,继续以下一个兄弟节点为节点计算
/**
 * @param {number} n
 * @param {number} k
 * @return {number}
 */
var findKthNumber = function (n, k) {
  const getCount = prefix => {
    let cur = prefix;
    let next = prefix + 1; //下一个前缀
    let count = 0;
    //当前的前缀当然不能大于上界
    while (cur <= n) {
      count += Math.min(n + 1, next) - cur; //下一个前缀的起点减去当前前缀的起点
      cur *= 10;
      next *= 10;
      // 如果说刚刚prefix是1,next是2,那么现在分别变成10和20
      // 1为前缀的子节点增加10个,十叉树增加一层, 变成了两层

      // 如果说现在prefix是10,next是20,那么现在分别变成100和200,
      // 1为前缀的子节点增加100个,十叉树又增加了一层,变成了三层
    }
    return count; //把当前前缀下的子节点和返回去。
  };

  let p = 1; //作为一个指针,指向当前所在位置,当p==k时,也就是到了排位第k的数
  let prefix = 1; //前缀
  while (p < k) {
    let count = getCount(prefix); //获得当前前缀下所有子节点的和
    if (p + count > k) {
      //第k个数在当前前缀下
      prefix *= 10;
      p++; //把指针指向了第一个子节点的位置,比如11乘10后变成110,指针从11指向了110
    } else if (p + count <= k) {
      //第k个数不在当前前缀下
      prefix++;
      p += count; //注意这里的操作,把指针指向了下一前缀的起点
    }
  }
  return prefix;
};

总结

这道题是以字典树为基础去做计算,注意一些细节,平时多熟悉类似题目。