题目
给定整数 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;
}
};
复制代码
结语
作者水平有限,如有不足欢迎指正;任何意见和建议欢迎评论区浏览讨论