440. 字典序的第K小数字

609 阅读2分钟

这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

440. 字典序的第K小数字

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

注意:1 ≤ k ≤ n ≤ 109。

示例 :

输入: n: 13 k: 2

输出: 10

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

解题思路

N个数字可以组织成为具有n个节点的10叉树,该树的前序遍历就是数字的字典序,如图所示

image.png

我们的目标就是需要找前序遍历的第k个节点

我们可以选择性的选择父节点遍历,不需要遍历前k个节点。

例如

输入:
n: 13   k: 6

输出:
2

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

我们可以通过计算出,以1开头有多少个节点,对比和k的关系。

  1. 以1为根节点的子树节点数量为5,如果k为6,说明不需要遍历以1为根节点的树,直接向左移动到以2为根节点的树上,继续遍历
  2. 以1为根节点的子树节点数量为5,如果k为2,说明我们需要遍历的节点必然在根节点1的子树上面,我们移动到根节点的最左子节点,继续遍历

如何计算子树的节点树

1629776269(1).png

从上图可以观察出,每一层节点的数字都是单调递增的,例如10,11....19,20,因此我们通过计算根节点的最左节点来固定每一层的节点数

每一层我们需要计算的节点数,位于上图红线划分的区间中。

代码

class Solution {
    public int findKthNumber(int n, int k) {

        long cur=1;
        k--;
        while (k>0)
        {
            int number = findingKthNumber(n, cur);
            if (k>=number)
            {
                k-=number;
                cur++;
            }else {
                k--;
                cur*=10;
            }
        }
        return (int)cur;


    }
    //sum 上一层的数字
    public int findingKthNumber(int n, long cur) {

        long next=cur+1;

        long res=0;
        while (cur<=n)
        {
            res+= Math.min(n-cur+1,next-cur);
            cur*=10;
            next*=10;
        }
        return (int)res;
    }
}