[Leetcode] 668. Kth Smallest Number in Multiplication Table 解题报告

题目

Nearly every one have used the Multiplication Table. But could you find out the k-th smallest number quickly from the multiplication table?

Given the height m and the length n of a m * n Multiplication Table, and a positive integer k, you need to return the k-th smallest number in this table.

Example 1:

Input: m = 3, n = 3, k = 5
Output: 
Explanation: 
The Multiplication Table:
1	2	3
2	4	6
3	6	9

The 5-th smallest number is 3 (1, 2, 2, 3, 3).

Example 2:

Input: m = 2, n = 3, k = 6
Output: 
Explanation: 
The Multiplication Table:
1	2	3
2	4	6

The 6-th smallest number is 6 (1, 2, 2, 3, 4, 6).

Note:

  1. The m and n will be in the range [1, 30000].
  2. The k will be in the range [1, m * n]

思路

这道题目很有意思。我记得Leetcode上有一道类似的题目,不过实在记不清楚原题的编号了。这里给出我想出来的三个解决方案,其中前两个解决方案都没法通过所有测试,但是可以用来开阔思路。

1、Naive Heap:

思路是我们将乘法表中的所有结果都提前存入一个小顶堆中,然后依次从堆中取数,当取到第k个元素时,返回当前被取出的堆顶元素即可。该算法的时间复杂度是O(m*n + klogmn),空间复杂度是O(m*n)。这个空间复杂度过高,造成“Memory Limit Exceeded”。

2、Smart Heap:

仔细观察乘法表可以发现,其每行、每列都是有序的。所以其实我们并不需要提前存储所有的乘法结果:当我们取出当前堆顶元素之后,下一个最小元素要么位于该元素的正右边,要么位于该元素的正下边。所以我们首先在小顶堆中只放入第1个元素,然后每次取出堆顶元素之后,在堆中放入它的正右边元素和正下边元素(如果有的话)。当取到第k个元素时返回即可。该算法的空间复杂度可以降低到O(m)(因为每行元素我们最多存一个)。时间复杂度也可以降低到O(klogm)。然而这个时间复杂度仍然不能通过所有测试数据,造成“Time Limit Exceeded”。

3、Binary Search:

杀手锏是使用二分查找法:在该乘法表中,最小值是1,最大值是m*n。所以我们初始化left = 1, right = m * n。每次首先得到left和right的中值mid,然后计算比mid小的数一种有多少个,而这个可以用O(m)的时间复杂度即可获得。如果比mid小的数小于k,说明我们要求的数在区间[mid + 1, right]内;否则说明我们要求的数在[left, mid - 1]。最终当left > right的时候,left即为我们所求。该算法的空间复杂度是O(1),时间复杂度是O(nlog(m*n))。由于在测试用例中k往往大于n,相当于n^2的量级,所以相比于Smart Heap,该算法的时间复杂度也要低一个量级。

代码

1、Naive Heap:

class Solution {
public:
    int findKthNumber(int m, int n, int k) {
        vector<int> nums;
        for (int r = 1; r <= m; ++r) {
            for (int c = 1; c <= n; ++c) {
                nums.push_back(-r * c);
            }
        }
        make_heap(nums.begin(), nums.end());
        int index = 0;
        while (true) {
            if (++index == k) {
                return -nums[0];
            }
            pop_heap(nums.begin(), nums.end());
            nums.pop_back();
        }
        return nums.back();
    }
};

2、Smart Heap:

class Solution {
public:
    int findKthNumber(int m, int n, int k) {
        vector<pair<int, int>> hp;
        hp.push_back(make_pair(1, 1));
        int index = 0;
        while (true) {
            pair<int, int> p = hp[0];
            pop_heap(hp.begin(), hp.end(), compare);
            hp.pop_back();
            if (++index == k) {
                return p.first * p.second;
            }
            if (p.second < n) {
                hp.push_back(make_pair(p.first, p.second + 1));
                push_heap(hp.begin(), hp.end(), compare);
            }
            if (p.second == 1 && p.first < m) {
                hp.push_back(make_pair(p.first + 1, 1));
                push_heap(hp.begin(), hp.end(), compare);
            }
        }
        return m * n;
    }
private:
    static bool compare(pair<int, int> &a, pair<int, int> &b) {
        return a.first * a.second > b.first * b.second;
    }
};

3、Binary Search:

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 num = 0;
            for (int r = 1; r <= m; ++r) {
                num += min(n, mid / r);
            }
            if (num < k) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        return left;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值