1.题目描述
给定整数 n 和 k,找到 1 到 n 中字典序第 k 小的数字。
注意:1 ≤ k ≤ n ≤ 109。
示例 :
2.思路
字典序就是根据数字的前缀进行排序,比如 10 < 9,因为 10 的前缀是 1,比 9 小。再比如 112 < 12,因为 112 的前缀 11 小于 12。
一个节点都拥有 10 个孩子节点,因为作为一个前缀 ,它后面可以接 0~9 这十个数字。而且可以发现,整个字典序排列也就是对十叉树进行先序遍历。1, 10, 100, 101, … 11, 110 …
要找到排在第k位的数,找到他的排位,需要三步:
(1)怎么确定一个前缀下所有子节点的个数?
给定一个前缀,返回下面子节点总数。用下一个前缀的起点减去当前前缀的起点,就是当前前缀下的所有子节点数总和。n是上界
int getcount(long prefix, long n){
long cur = prefix;
long next = cur + 1;
long count = 0;
while(cur <= n){
count += min(n + 1, next) - cur;
cur *= 10;
next *= 10;
}
return count;
}
(2)如果第 k 个数在当前的前缀下,怎么继续往下面的子节点找?
无非就是往子树里面去看。prefix *= 10 就可以了
(3)如果第 k 个数不在当前的前缀,即当前的前缀比较小,如何扩大前缀,增大寻找的范围?
当前的前缀小了,扩大前缀 prefix++
3.代码
class Solution {
public:
int findKthNumber(int n, int k) {
long p = 1;
long prefix = 1;
while(p < k){
long count = getcount(prefix, n);//获得当前前缀下的所有子节点数
if(p + count > k){//说明第k个数在这个前缀范围内
prefix *= 10;
p++;
}
else{//前缀需要扩大
prefix++;
p += count;
}
}
return (int)prefix;
}
int getcount(long prefix, long n){
long cur = prefix;
long next = cur + 1;
long count = 0;
while(cur <= n){
count += min(n + 1, next) - cur;
cur *= 10;
next *= 10;
}
return count;
}
};