题目
给定整数 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 <= 10^9
思路
很好的题目,一开始就想到了这其实是一个多叉树的遍历问题,构造出来是一个字典树,如下图所示(画的有点丑…),图中的为
,
为
。前序遍历第
个节点
就是答案。

因为的数据范围,所以肯定不能直接挨个节点遍历。
我们可以发现,对于第小的节点
,往后再数
个就是答案。记以节点
为根节点的子树一共有
个节点。
如果,那么,第
小的数肯定不在这个子树中,直接跳到
相邻的节点
,反之,在该子树中,
更新为左边第一个孩子,即
。
通过这个思路,我们可加快寻找第小的数,具体实现见代码。
代码
class Solution {public int findKthNumber(int n, int k) {// 当前数字,可以看做是一个前序遍历的指针int p = 1;// 当前数字是第i小的int i = 1;// 要找第k小的while (i < k) {int cnt = getChildNum(p, n);// 对应思路中的分析// 如果cnt <= k - i 第k个肯定不在当前子树中 去p右边相邻的兄弟节点寻找 所以p++if (cnt <= k - i) {p++;i += cnt;} else {// cnt > k - i 第k个在当前子树中 在当前子树中寻找 先看左边第一个孩子节点 所以p*=10p *= 10;i++;}}return p;}// 计算以p为根节点的子树的节点个数(包括p节点)private int getChildNum(long p, int n) {// 一层一层累加计数的思路计算节点个数// 第一层1个 第二层10个 第三层100个// cnt为累加的节点个数 最后的返回值int cnt = 0;// p指向当前层第一个节点 last指向最后一个long last = p;while (p <= n) {// 将当前层个数累加cnt += Math.min(last, n) - p + 1;// 更新p和lastp *= 10;last = 10 * last + 9;}return cnt;}}
