vlambda博客
学习文章列表

[二分查找] 乘法表中第k小的数

 二分查找系列专题   LeetCode668

题目描述

几乎每一个人都用 乘法表。但是你能在乘法表中快速找到第k小的数字吗?

给定高度m 、宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字。

示例:

输入: m = 3, n = 3, k = 5
输出: 3
解释:
乘法表:
1 2 3
2 4 6
3 6 9

第5小的数字是 3 (1, 2, 2, 3, 3).

注意

  • mn 的范围在 [1, 30000] 之间。
  • k 的范围在 [1, m * n] 之间。

题解

二分查找系列是我搜罗的二分查找系列的难题集合,因此今后不再讨论简单的暴力解法(无法AC且没太大意义)。

我点开这道题,它没有时间复杂度要求,方方正正的乘法表仿佛在告诉我用遍历去做。我想这个困难题不可能这么没有营养,我绞尽脑汁,仔细看了前人留下来的题解,才恍然大悟,大家都告诉我要用“二分”。

这个题和一般的二分法思路还稍有区别,二分的对象是乘法表里面的乘积。比如上面的示例中给出的3行3列的乘法表,最小值为1,最大值为3*3=9,那二分的初始边界就是1和9,详细的搜索步骤为:

  • left=1right=9,求得 mid=5;计算乘法表中小于等于5的数字的个数为 6,现要求第5小的数字,显然这个数字应该在[1, 5]之间;
  • left=1right=5,求得 mid=3;计算乘法表中小于等于3的数字的个数为 5,现要求第5小的数字,显然这个数字应该在[1, 3]之间;
  • left=1right=3,求得 mid=2;计算乘法表中小于等于2的数字的个数为 3,现要求第5小的数字,显然这个数字应该在[3, 3]之间;
  • left == right == 3,查询结束,返回3。


问:以上的思路很清晰吧,那么如何计算乘法表中小于等于target的数字的个数呢?

观察得到第i行的数字规律为 ,因此从上往下依次遍历每一行,按照如下方式计算:

// mid / i 表示第i行中小于等于mid的数字的个数,n表示列数
cnt += min(mid / i, n);


最终代码如下:

class Solution {
public:
int findKthNumber(int m, int n, int k) {
int left = 1, right = m * n;
while (left < right) {
int mid = left + (right - left) / 2;
int cnt = 0;
for (int i = 1; i <= m; i++) {
cnt += min(n, mid / i);
}
if (cnt >= k) right = mid;
else left = mid + 1;
}
return left;
}
};



 二分查找系列往期题目