LeetCode打卡day23——字典序的第K小数字

480 阅读1分钟

“Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。”

一、题目描述:

440. 字典序的第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

二、思路分析:

首先什么是字典序?

例如 为什么 112 放在 12 前面?因为首先他们的位数不一样,比较的前缀位数就位最小的位数,即比较 11 和12 ,故112放在12前面。

再看 为什么1 在10前面?因为虽然他们的前缀一样,但1的位数比10少。当位数一样时即10和11,比较最开始不相同的位数的值的大小。

构建字典树

通过上面的规则,我们可以知道,1可以组成一个前缀,有1019;10可以组成前缀,有100109;100可以组成前缀有 10001009 ......即每个数作为前缀都有下一位都有09,而组成的数又可以作为前缀,我们可以用一个10叉树来描述他们

image.png 可以发现 同一层的数他们的位数相同,进行先序遍历,得到的就是字典序

求字典树结点下的所有子节点(包括该节点)

我们要判断第k个数在哪,则必须知道每个前缀下的子节点数目。

我们按每层结点数相加最后得到结果,那么我们如何得到该层的节点数呢?因为它可能是不满的。对于前缀1,它的同层下一个前缀为1+1=2,节点数number+2-1。前缀1子节点最先是10=1* 10,前缀2子节点最先为20=2* 10,若子节点是满的则count+两者直接相减,否则即n在当前位置,要包含n故count+n+1-10。

代码如下

const getNumber=function(prefix,n){
       let cur=prefix,next=prefix+1,number=0
       while(cur<=n){
           number+=Math.min(n+1,next)-cur
           cur*=10
           next*=10
       }
       return number
   }

判断第k小数字位置

由题可知最开始第一小的数字为1,用p表示第p小的数字,用prefix表示当前前缀,prefix=1

首先根据n的大小,判断当前prefix下子节点的个数number(包含第p小的数字),故p+number-1表示已经遍历的数字范围+下一个区域范围,

  • 若k<=p+number-1,则k肯定在这个范围里面,prefix*=10,p++,表示从当前前缀第一个子节点缩小范围,
  • 否则,k不在这个范围里面,要扩大,故prefix++,p+=number,而不是加number-1,因为加number-1只表示当前前缀子节点的最后一个节点,并没有进入新的前缀

代码如下

let p=1,prefix=1
    while(p<k){
        let number=getNumber(prefix,n)
        if(k>=p+number){
            prefix++
            p+=number
        }else{
            prefix*=10
            p++
        }
    }

三 Ac代码

/**
 * @param {number} n
 * @param {number} k
 * @return {number}
 */
var findKthNumber = function(n, k) {
    const getNumber=function(prefix,n){
        let cur=prefix,next=prefix+1,number=0
        while(cur<=n){
            number+=Math.min(n+1,next)-cur
            cur*=10
            next*=10
        }
        return number
    }
    let p=1,prefix=1
    while(p<k){
        let number=getNumber(prefix,n)
        if(k>=p+number){
            prefix++
            p+=number
        }else{
            prefix*=10
            p++
        }
    }
    return prefix
};

总结

学会了 字典序,如何构建字典树,如何求字典树节点的子节点数