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

91 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目来源:leetcode.cn/problems/kt…

大致题意: 给整数 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,右边界为 m*n

代码:

	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;
    }