[LeetCode] Kth Smallest Number in Multiplication Table 乘法表中的第K小的數字


 

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]

 

這道題跟之前那道Kth Smallest Element in a Sorted Matrix沒有什么太大的區別,這里的乘法表也是各行各列分別有序的。那么之前帖子里的方法都可以拿來參考。之前帖子中的解法一在這道題中無法通過OJ,維護一個大小為k的優先隊列實在是沒有利用到這道題乘法表的特點,但是后兩種解法都是可以的。為了快速定位出第K小的數字,我們采用二分搜索法,由於是有序矩陣,那么左上角的數字一定是最小的,而右下角的數字一定是最大的,所以這個是我們搜索的范圍,然后我們算出中間數字mid,由於矩陣中不同行之間的元素並不是嚴格有序的,所以我們要在每一行都查找一下mid,由於乘法表每行都是連續數字1,2,3...乘以當前行號(從1開始計數),所以我們甚至不需要在每行中使用二分查找,而是直接定位出位置。具體做法是,先比較mid和該行最后一個數字的大小,最后一數字是n * i,i是行數,n是該行數字的個數,如果mid大的話,直接將該行所有數字的個數加入cnt,否則的話加上mid / i,比如當前行是2, 4, 6, 8, 10,如果我們要查找小於7的個數,那么就是7除以2,得3,就是有三個數小於7,直接加入cnt即可。這樣我們就可以快速算出矩陣中所有小於mid的個數,根據cnt和k的大小關系,來更新我們的范圍,循環推出后,left就是第K小的數字,參見代碼如下:

 

解法一:

 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, cnt = 0;
             for (int i = 1; i <= m; ++i) {
                 cnt += (mid > n * i) ? n : (mid / i);
             }
             if (cnt < k) left = mid + 1;
             else right = mid;
         }
         return right;
     }
 };

 

下面這種解法在統計小於mid的數字個數的方法上有些不同,並不是逐行來統計,而是從左下角的數字開始統計,如果該數字小於mid,說明該數字及上方所有數字都小於mid,cnt加上i個,然后向右移動一位繼續比較。如果當前數字小於mid了,那么向上移動一位,直到橫縱方向有一個越界停止,其他部分都和上面的解法相同,參見代碼如下:

 

解法二:

 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, cnt = 0, i = m, j = 1;
             while (i >= 1 && j <= n) {
                 if (i * j <= mid) {
                     cnt += i;
                     ++j;
                 } else {
                     --i;
                 }
             }
             if (cnt < k) left = mid + 1;
             else right = mid;
         }
         return right;
     }
 };

 

下面這種解法由網友bambu提供,是對解法二的優化,再快一點,使用除法來快速定位新的j值,然后迅速算出當前行的小於mid的數的個數,然后快速更新i的值,這比之前那種一次只加1或減1的方法要高效許多,感覺像是解法一和解法二的混合體,參見代碼如下:

 

解法三:

 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, cnt = 0, i = m, j = 1;
            while (i >= 1 && j <= n) {
                 int t = j;
                 j = (mid > n * i) ? n + 1 : (mid / i + 1);
                 cnt += (j - t) * i;
                 i = mid / j;
             }
            if (cnt < k) left = mid + 1;
            else right = mid;
        }
        return right;
     }
 };

 

類似題目:

Kth Smallest Element in a Sorted Matrix

Find K-th Smallest Pair Distance

 

參考資料:

https://discuss.leetcode.com/topic/101194/my-8-lines-c-solution

https://discuss.leetcode.com/topic/101132/java-solution-binary-search

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM