力扣 668. 乘法表中第k小的数

题目来源:https://leetcode.cn/problems/kth-smallest-number-in-multiplication-table/

大致题意:
给整数 m 、n 和 k,表示有一个 m*n 的乘法表矩阵,求乘法表中第 k 小的数

思路

给定一个数 t,如果要在给定的乘法表中求小于等于 t 的数个数,可以通过如下方法统计

一行一行的进行比较,设当前比较的行数为 i,乘法表的列数为 r,那么

  • 若 t > i * r,则该行元素全部小于 t
  • 若 t <= i * r,如果 t 可以被 i 整除(也就是该行有等于 t 的元素),那么小于 t 的元素有 t / i - 1 个,也就是说小于等于 t 的数有 t / i 个;如果不可以被整除,小于等于 t 的个数也是 t / i 个

于是可以通过二分枚举所有数,直到枚举的数是乘法表中的第 k 小的数为止,这样时间复杂度为 O(min(m, n)log(mn))

具体的执行逻辑可以概括为:

  1. 初始化二分的左边界为 1,右边界为 m*n
  2. 获取当前区间中值,使用上述方法统计乘法表中小于等于中值的元素个数
  3. 如果当前乘法表中小于等于中值的元素个数大于等于 k,表示当前中值大于等于第 k 小的数,使用当前中值作为新的右边界;否则,表示当前中值小于第 k 小的数,应该往更大的区间找,使用中值 + 1 作为新的左边界
  4. 重复 2 、3步,直至当前左右边界相等

可以注意到,比较时所取的中值可能不在乘法表对应的积中,那么此时中值与所求积的关系有两种

  1. 中值大于所求积,那么会更新右边界为中值,也就会缩小下次计算的中值
  2. 中值大于所求积,那么会更新左边界为中值 + 1,也就会增加下次计算的中值

以上两种情况会不断缩小查询区间,迫使区间边界向中值靠近,直至区间长度 1,此时区间内的值也就为所求积

代码:

	public int findKthNumber(int m, int n, int k) {
        int min = Math.min(m, n);   // 求出 m 和 n 中的最小值,使用最小值做乘法表的行
        int max = Math.max(m, n);   // 求出 m 和 n 中的最大值,使用最大值做乘法表的列
        // 二分边界
        int l = 1;
        int r = m * n;
        while (l < r) {
            // 当前边界的中间值
            int mid = (l + r) >> 1;
            // 获取乘法表小于等于中间值的数的个数
            int count = getCount(mid, min, max);
            // 如果个数小于 k,表示当前中间值比第 k 小的数小,更新左边界
            if (count < k) {
                l = mid + 1;
            } else {    // 个数大于等于 k,表示当前中间值大于等于第 k 小的数,更新右边界
                r = mid;
            }
        }
        return l;
    }

    public int getCount(int mid, int min, int max) {
        int a = 0;
        // 获取乘法表小于等于中间值的数的个数
        for (int i = 1; i <= min; i++) {
            // 当前行最大值小于 mid,直接统计该行所有元素
            if (i * max < mid) {
                a += max;
            } else {    // 否则,统计小于等于 mid 的元素个数
                a += mid / i;
            }
        }
        return a;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值