学习算法:字典序的第k小数字

4 篇文章 0 订阅

题目如下:

给定整数 n 和 k,找到 1 到 n 中字典序第 k 小的数字。

注意:1 ≤ k ≤ n ≤ 109

这道题的难点在于字典序,字典序是按照前缀大小来排序的,比如11和2,实际11排在2的前面,和正常数字排序很不一样,因此需要抽象出字典序模型,即实际上是一个十叉树,从网上找了个图如下

因此只需要确定三件事就可以确定k的位置即:

怎么确定一个前缀下所有子节点的个数?
如果第 k 个数在当前的前缀下,怎么继续往下面的子节点找?
如果第 k 个数不在当前的前缀,即当前的前缀比较小,如何扩大前缀,增大寻找的范围?

确定前缀下所有子节点个数可通过一直往下层节点遍历,直到前缀达到最大值n时下一个前缀减去当前前缀来获得,当然要考虑n在当前前缀的情况和到下一个前缀的情况,即边界情况。

第一步这里我想了很久,后面才想明白,n即具体的数字,前缀也是具体的数字,只不过前缀下面也会有子节点。比如说前缀为11,则下面还有110,111等节点,如果n为123,即前缀11的下一个前缀12的下层第三个节点。

第二步,如果k在当前的前缀下,比如说123在12的前缀下,则树的深度需要往下一层,即12*10,同时当前位置也要加一,因为往下一层,节点也会+1

第三步,如果k不在当前的前缀下,比如123 不在120的前缀下,则前缀需要往后+1,同时当前位置数需要加上当前前缀和下一个前缀中间的个数

如此一直往后找,即可找到第k个数

附上代码片段

    public int findKthNumber(int n, int k) {
        // 当前位置,从1开始
        int curr = 1;
        // 前缀
        int prefix = 1;
        // 当前位置小于k则一直遍历
        while (curr < k) {
            // 每次查找当前前缀到下一个前缀且不大于n的节点的个数,即prefix峰的值
            int temp = count(n, prefix);
            // 如果当前位置加上prefix峰的数量大于k,则说明k就在当前子节点下面,则树往下探一层,同时当前位置+1
            if (temp + curr > k) {
                prefix*=10;
                curr++;
            } else { // 否则说明k不在当前前缀下面,则前缀+1,同时当前位置加上prefix峰的值
                prefix++;
                curr+=temp;
            }
        }
        return prefix;
    }

    public int count(int n, int prefix) {
        long curr = prefix;
        // 下一个前缀峰头
        long next = prefix + 1;
        int count = 0;
        while (curr <= n) {
            // 两个峰头中间的个数,如果不够数量,那最大应该是n
            count += Math.min(n+1, next) - curr;
            // 如果说刚刚prefix是1,next是2,那么现在分别变成10和20
            // 1为前缀的子节点增加10个,十叉树增加一层, 变成了两层

            // 如果说现在prefix是10,next是20,那么现在分别变成100和200,
            // 1为前缀的子节点增加100个,十叉树又增加了一层,变成了三层
            curr*=10;
            next*=10;
        }
        return count;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值