一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情。
每日刷题 2021.04.20
- leetcode原题链接:leetcode-cn.com/problems/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
解题思路
- 根据题意分析:主要分两部分来处理
- 计算一个节点下有多少节点(包括当前节点)
- 类似的前序遍历(VLR)来计算k位置的值
如何计算节点下有多少个节点?
- 简单画图可以发现一个节点,比如1,子节点是10-19,这中间的值字典表中都要小于2。
- 继续往后推,10节点的子节点是110-119,这些节点小于12,而12的子节点是120-129,以此类推。
- 可以得到某个节点的子节点n的所有子节点是 n10+9 - n10 + (n10+9)10+9 - (n1010)。
- 每一层的最小值都是上一层的最小值10,最大值是上一层的最大值10+9。
- 注意边界是n,如果最小节点小于n则说明已经统计了所有节点了,而且需要注意n大于最小值且小于最大值的情况。
如何前序遍历(VLR)
- 根据题意前序遍历的结果就是排序之后的结果,取k位置的值就行了。
- 有了第一部分,我们就能方便的算出一个节点有多少节点了。所以先获取最开始节点有多少个子节点。
- 如果子节点数大于k,也就是k在当前节点内,则往下遍历(LR),这个时候需要节点*10获取子节点的最小值,注意k需要-1,因为要排掉当前节点(V)。
- 如果子节点小于k,也就是说k在当前节点后面,当前节点+1,k减去当前节点的节点数。
- k等于0的时候,即找到节点。
AC代码
var findKthNumber = function(n, k) {
const getCount = (root, n) => {
let result = 0
let f = root
let l = root
while (f <= n) {
result += Math.min(n, l) - f + 1
f *= 10
l *= 10
l += 9
}
return result
}
let cur = 1
k -= 1
while (k > 0) {
const count = getCount(cur, n)
if (count > k) {
cur *= 10
k -= 1
} else {
cur += 1
k -= count
}
}
return cur
};
总结
- 字典树比较复杂,常常在必须要用的时候才使用。