[leetcode-400]字典序的第K小数字

题目

给定整数  n  和  k,返回   [1, n]  中字典序第  k  小的数字。

示例 1

输入: n = 13, k = 2
输出: 10
解释: 字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9],所以第二小的数字是 10。

复制代码

示例 2

输入: n = 1, k = 1
输出: 1
复制代码

提示

  • 1 <= k <= n <= 109

题解

什么是题目越简单,事越大,这就是;题目一句话,题解想半天。

多叉树前序遍历


graph TD
root --> 1
root --> 2
root --> 3[...]
root --> 9
1 --> 10
1 --> 11
1 --> 12[...]
1 --> 19
10 --> 100
10 --> 101
10 --> 102[...]
10 --> 109

  • 从 1 到 n 的字典序可以看作上述不完全 9 叉树的节点;
  • 从上述 9 叉树节点前序遍历的第 k 个节点即为字典序第 k 小的数

上述图文已经给出了解决思路,下面就是将思路转换为代码

  • 直接构建 1 到 n 的 9 叉树可以吗? 可以是可以,但是复杂度有点高,只需要找到第 k 个树却构建完整的树,得不偿失
  • 可以利用树的特性模拟前序遍历,降低时间复杂度

如何模拟?

  • 假如现在有一个方法 getChildren,可以得到当前节点下有多少子节点 child 。

    • 比如 1 到 100,1 这个节点下有 12 个节点
  • 假设子节点数量 child 小于 k

    • 说明当前节点包含所有子节点不是答案。答案在当前节点的其它兄弟节点中。
    • k 减去 child; 并且在当前节点的兄弟节点找子节点数量是否小于 k-child
    • 重复上述操作
  • 如果 子节点数量 child >= k

    • 答案一定存在在当前节点或者当前节点的子节点中
    • 递归找子节点的子节点数量与 k-1 的关系
    • 重复上述操作

    根据上述思路编辑代码如下:

    完整代码

var findKthNumber = function (n, k) {
  let count = 1;
  k--;
  while (k > 0) {
    const children = getChildren(count);
    if (children <= k) {
      k -= children;
      count++;
    } else {
      count = count * 10;
      k--;
    }
  }
  return count;
  function getChildren(c) {
    let child = 0;
    let left = c;
    let right = c;
    while (left <= n) {
      child += Math.min(right, n) - left + 1;
      left *= 10;
      right = right * 10 + 9;
    }
    return child;
  }
};
复制代码

结语

作者水平有限,如有不足欢迎指正;任何意见和建议欢迎评论区浏览讨论

猜你喜欢

转载自juejin.im/post/7078246470914146312
今日推荐