题目链接

LeetCode

题目描述

给定整数 nk,找到 1n 中字典序第 k 小的数字。

注意:1 ≤ k ≤ n ≤ 109。

示例 :

输入:
n: 13 k: 2

输出:
10

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

解题思路

方法一:字典树(十叉树)

a087ef3df33a26210275027ca6cafb03168cf0865fadc611c8ca7af41ab8b226-bad2ce390cb0c417bd81e1f2b17e0e7eadba1159bc13c43cf7bf72bd55ddb69b-截屏2020-05-09 上午11.48.14.png
e629f75af562f93ab02e853c59ad8bbccd3d080252a0e01b2aa226ce6eb7fcae-截屏2020-05-09 上午11.48.24.png

  1. class Solution {
  2. public int findKthNumber(int n, int k) {
  3. // 当前字典树结点 (当前遍历到的数字)
  4. int cur = 1;
  5. // 到达第 k 个节点需要遍历 k-1 个结点
  6. // 当前 k 值表示剩余需要遍历的节点数
  7. k -= 1;
  8. while(k > 0){
  9. int nodes = getNodes(n, cur);
  10. // 如果当前子树的结点数小于等于 k, 则需要全部遍历
  11. if(nodes <= k){ // 向右侧节点走
  12. // 减去已经遍历过的子树
  13. k -= nodes;
  14. // 指向下一个新的结点
  15. ++cur;
  16. }else{ // 向下往最左孩子节点走:字典序上升1位
  17. // 结果在当前子树中, 减去当前根节点
  18. --k;
  19. // 向下一层搜索
  20. cur *= 10;
  21. }
  22. }
  23. return (int)cur; // 最后cur停在的数字就是字典序的第K小数字
  24. }
  25. // 获取 cur 值下子树结点的个数
  26. private int getNodes(int n, long cur){
  27. long nodes = 0;
  28. long next = cur + 1;
  29. while(cur <= n){
  30. // 如果当前一层中有定值 n, 则只能取到n,末位从 0 开始
  31. nodes += Math.min(n - cur + 1, next - cur);
  32. // 计算下一层个数
  33. next *= 10;
  34. cur *= 10;
  35. }
  36. // 返回当前 cur 值下子树结点个数
  37. return (int)nodes;
  38. }
  39. }
  • 时间复杂度 O(log n)
  • 空间复杂度 O(1)