440. 字典序的第K小数字(困难)-字典树-数节点-字节跳动高频题

248 篇文章 2 订阅
232 篇文章 0 订阅

一、题目描述

给定整数 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
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

二、解题

字典树

这题首先看386题。这题找第k小的数字,其实就是在树中找每个节点的子节点数量,然后与K值相比较。
在这里插入图片描述
其中以第一层第一个节点1计算,看以1为节点的下面有多少个子节点。若当前节点的子节点数大于K,则说明目标值就在该子树下面,然后一层一层往下面找,更新节点值,1->10,也需要更新K值,也就是第二次的第一个数0,然后计算该0下面有多少个子节点值,不停的执行,直到找到最终的值。
若当第一层的第一个节点1值下面的子节点的数量小于K值,说明在节点2下面找,更新K值,依次执行。

如果就在第一层的第一个节点下面,就依次每层往下找;

class Solution {
    public int findKthNumber(int n, int k) {
        long cur = 1;
        k--;
        while(k > 0){
            int nodes = countNodes(n,cur);
            if(k >= nodes){
                k = k - nodes;
                cur++;
            }else{
                k--;
                cur *= 10;
            }
        }
        return (int) cur;
    }

    public int countNodes(long n,long cur){
        long total = 0;
        long next = cur + 1;
        while(cur <= n){
            total += Math.min(n - cur + 1,next -cur);
            cur *= 10;
            next *= 10;
        }
        return (int) total;
    }
}

在这里插入图片描述

注释:

class Solution {
    public int findKthNumber(int n, int k) {
        /*
        参考评论区郭郭的视频题解:
        本质是一个10叉树的先序遍历,找到按照先序遍历的第k个节点
        为什么是先序遍历?这个由字典序的性质决定:[1,10,100,1000,1001]
        假设相同位数的数字在10叉树的同一层上,那么就是先序遍历就是字典序排列
        从cur=1开始进行遍历,先计算的以cur为根的且<=n的节点个数nodes
            若nodes<=k,说明以cur开头的合格节点数不够,cur应该向右走:cur++
            若nodes>k,说明以cur开头的合格节点数足够,cur应该向下走:cur*=10
        */
        // 首先遍历以1开头大的数字
        // 由于cur可能会很大,因此int可能计算过程中会溢出,用long类型
        long cur = 1;
        // 因为1遍历了,因此k--
        k--;
        // 当且仅当k>0(还未遍历到第k个的时候)循环
        while(k > 0) {
            // 获取以cur开头的子节点合格(<=n)数目nodes
            int nodes = getNodes(n, cur);
            // 若nodes<=k的话说明把这nodes个节点分完都还没到k
            if(nodes <= k) {
                // cur向右走
                cur++;
                // 抵消掉nodes个节点
                k -= nodes;
            }else {
                // 若nodes>k的话说明把nodes个节点够分
                // cur往下走
                cur *= 10;
                // 将cur计算进k
                k--;
            }
        }
        // 最后cur会停留在第k小的数上
        return (int)cur;
    }

    /*
    计算[1,n]内以cur为根(开头)的节点个数
    */
    private int getNodes(int n, long cur) {
        // next表示cur右边的数,此时cur=10,next=11
        long next = cur + 1;
        // 统计合格的节点个数
        long totalNodes = 0;
        // 当cur<=n时可以进入循环
        while(cur <= n) {
            // 这里是最关键的一步:当n不在cur层时,该层有效节点数目为next - cur(全部都要了)
            // 当n在cur层时,该层有效节点数目为n - cur + 1(要一部分)
            // 统一起来就是取最小值
            totalNodes += Math.min(n - cur + 1, next - cur);
            // cur与next均向下计算
            cur *= 10;
            next *= 10;
        }
        return (int)totalNodes;
    }
}

输出测试一下:

代码:

class Solution {
    public int findKthNumber(int n, int k) {
        long cur = 1;
        k--;
        System.out.println("0->k:"+k);
        while(k > 0){
            int nodes = countNodes(n,cur);
            System.out.println("nodes:"+nodes);
            if(k >= nodes){
                k = k - nodes;
                System.out.println("1->k:"+k);
                cur++;
                System.out.println("1->cur:"+cur);
            }else{
                k--;
                System.out.println("2->k:"+k);
                cur *= 10;
                System.out.println("2->cur:"+cur);
            }
        }
        return (int) cur;
    }

    public int countNodes(long n,long cur){
        long total = 0;
        long next = cur + 1;
        while(cur <= n){
            //计算每一层的节点数,然后统计初始cur值下小于n的节点数
            total += Math.min(n - cur + 1,next -cur);
            System.out.println("total:"+total);
            cur *= 10;
            next *= 10;
        }
        return (int) total;
    }
}
0->k:65
total:1
total:11
total:35
nodes:35
1->k:30
1->cur:2
total:1
total:11
nodes:11
1->k:19
1->cur:3
total:1
total:11
nodes:11
1->k:8
1->cur:4
total:1
total:11
nodes:11
2->k:7
2->cur:40
total:1
nodes:1
1->k:6
1->cur:41
total:1
nodes:1
1->k:5
1->cur:42
total:1
nodes:1
1->k:4
1->cur:43
total:1
nodes:1
1->k:3
1->cur:44
total:1
nodes:1
1->k:2
1->cur:45
total:1
nodes:1
1->k:1
1->cur:46
total:1
nodes:1
1->k:0
1->cur:47

分析:第一层第一个节点1的子节点数量为35,小于K值,则继续在节点2中查找,更新k值为30,然后节点2值下面的子节点数量为11,小于30,则继续在节点3中查找,然后更新K值为19,依次进行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值