440. 字典序的第K小数字
听说此题是字节跳动最常考题之一(Top10)
什么是字典序
- 在做这个题目之前我们需要了解什么是字典序:其实就是根据数字前缀进行排序,两个数字比较,首先比较第一位数字,第一位数字小的字典序就小,如果相同就继续比较下一位数字,举个例子10<9因为10第一位是1,9的第一位是9,再者123<22
- 如下图我们发现,每一个节点都有十个节点,相当于一个十叉树,对这棵树进行前序遍历就是我们需要的字典序从小到大排序
解题步骤
- 首先要确定指定前缀下所有的子节点数,那么就用下一个前缀和的起点减去当前前缀的起点就是当前前缀下所有子节点数总和,这里有一种情况就是它不是一颗满十叉树,那么计算总数的时候就稍微处理一下,用min(n+1,next)减去当前cur,这里解释一下为什么用n+1,因为假设n=12,那么此时以1为前缀的节点数为1,10,11,12四个,如果n不加一减去cur加上根节点就会是3个
- 那么如果第k小的节点在当前前缀下,我们直接去下一层找,假设当前前缀为p,操作就是p*10,如果不在当前前缀下的话,我们去当前前缀的下一个前缀,即扩大前缀,操作为p++
AC代码
class Solution{
public:
typedef long long ll;
ll getnum(ll pre, ll n){
ll cur = pre;//pre是前缀,n是上界
ll next = pre + 1;//代表下一个前缀
ll count = 0;
while (cur <= n){//当前的前缀不能大于上界
count += min(next, n + 1) - cur;//下一个前缀的起点减去当前前缀的起点
cur *= 10;//直接进入下一层,因为它是一个十叉树,乘以十就进入了下一层
next *= 10;//同理
}
return count;//返回当前前缀下的子节点总数
}
int findKthNumber(int n, int k){
ll p = 1;//指向当前所在的位置
ll pre = 1;//前缀
while (p < k){
ll count = getnum(pre, n);//获得当前前缀下所有子节点的和
if (p + count > k){//如果第k个数在当前前缀下
pre *= 10;
p++;
}
else if (p + count <= k){//如果不在
pre++;//扩大前缀
p += count;//将指针指向扩大后前缀的起点
}
}
return pre;
}
};
成熟的意义在于,你可以试着理解原来不理解的事,尝试原谅你以前从不曾接受的人,在难过和委屈面前,还依旧可以保持乐观的态度独立的人格和勇敢善良的心 |
---|