A sorted list A
contains 1, plus some number of primes. Then, for every p < q in the list, we consider the fraction p/q.html
What is the K
-th smallest fraction considered? Return your answer as an array of ints, where answer[0] = p
and answer[1] = q
.git
Examples: Input: A = [1, 2, 3, 5], K = 3 Output: [2, 5] Explanation: The fractions to be considered in sorted order are: 1/5, 1/3, 2/5, 1/2, 3/5, 2/3. The third fraction is 2/5. Input: A = [1, 7], K = 1 Output: [1, 7]
Note:github
A
will have length between 2
and 2000
.A[i]
will be between 1
and 30000
.K
will be between 1
and A.length * (A.length - 1) / 2
.
這道題給了咱們一個有序數組,裏面是1和一些質數,說是對於任意兩個數,均可以組成一個 [0, 1] 之間分數,讓求第K小的分數是什麼,題目中給的例子也很好的說明了題意。那麼最直接暴力的解法就是遍歷出全部的分數,而後再進行排序,返回第K小的便可。可是這種無腦暴力搜索的方法 OJ 是不答應的,無奈,只能想其餘的解法。因爲數組是有序的,因此最小的分數確定是由第一個數字和最後一個數字組成的,而接下來第二小的分數就不肯定是由第二個數字和最後一個數字組成的,仍是由第一個數字跟倒數第二個數字組成的。這裏用一個最小堆來存分數,那麼每次取的時候就能夠將最小的分數取出來,因爲前面說了,不能遍歷全部的分數都存入最小堆,那麼該怎麼辦呢,能夠先存n個,哪n個呢?其實就是數組中的每一個數字都和最後一個數字組成的分數。因爲須要取出第K小的分數,那麼在最小堆中取K個分數就能夠了,第一個取出的分數就是那個由第一個數字和最後一個數字組成的最小的分數,而後就是精髓所在了,此時將分母所在的位置前移一位,仍是和當前的分子組成新的分數,這裏即爲第一個數字和倒數第二個數字組成的分數,存入最小堆中,那麼因爲以前已經將第二個數字和倒數第一個數字組成的分數存入了最小堆,因此不用擔憂第二小的分數不在堆中,這樣每取出一個分數,都新加一個稍稍比取出的大一點的分數,這樣取出了第K個分數即爲所求,參見代碼以下:數組
解法一:ide
class Solution { public: vector<int> kthSmallestPrimeFraction(vector<int>& A, int K) { priority_queue<pair<double, pair<int, int>>> q; for (int i = 0; i < A.size(); ++i) { q.push({-1.0 * A[i] / A.back(), {i, A.size() - 1}}); } while (--K) { auto t = q.top().second; q.pop(); --t.second; q.push({-1.0 * A[t.first] / A[t.second], {t.first, t.second}}); } return {A[q.top().second.first], A[q.top().second.second]}; } };
其實這道題比較經典的解法是用二分搜索法 Binary Search,使用的二分搜索法是博主概括總結帖 LeetCode Binary Search Summary 二分搜索法小結 中的第四種,即二分法的斷定條件不是簡單的大小關係,而是能夠抽離出子函數的狀況,下面來看具體怎麼弄。這種高級的二分搜索法在求第K小的數的時候常常使用,好比 Kth Smallest Element in a Sorted Matrix,Kth Smallest Number in Multiplication Table,和 Find K-th Smallest Pair Distance 等。思路都是用 mid 看成 candidate,而後統計小於 mid 的個數 cnt,和K進行比較,從而肯定折半的方向。這道題也是如此,mid 爲候選的分數值,剛開始時是 0.5,而後須要統計出不大於 mid 的分數都個數 cnt,同時也須要找出最接近 mid 的分數,看成返回的候選值,由於一旦 cnt 等於K了,直接將這個候選值返回便可,這個候選值分數是由p和q來表示的,其中p表示分子,初始化爲0,q表示分母,初始化爲1(由於除數不能爲0),在內部的 while 循環退出時,分數 A[i]/A[j] 就是最接近 mid 的候選者,此時假如 p/q 要小於 A[i]/A[j],就要分別更新p和q。不然若是 cnt 小於K,說明應該增大一些 mid,將 left 賦值爲 mid,反之若是 cnt 大於K,須要減少 mid,將 right 賦值爲 mid,參見代碼以下:函數
解法二:post
class Solution { public: vector<int> kthSmallestPrimeFraction(vector<int>& A, int K) { double left = 0, right = 1; int p = 0, q = 1, cnt = 0, n = A.size(); while (true) { double mid = left + (right - left) / 2.0; cnt = 0; p = 0; for (int i = 0, j = 0; i < n; ++i) { while (j < n && A[i] > mid * A[j]) ++j; cnt += n - j; if (j < n && p * A[j] < q * A[i]) { p = A[i]; q = A[j]; } } if (cnt == K) return {p, q}; if (cnt < K) left = mid; else right = mid; } } };
Github 同步地址:url
https://github.com/grandyang/leetcode/issues/786spa
相似題目:code
Find K Pairs with Smallest Sums
Kth Smallest Element in a Sorted Matrix
Kth Smallest Number in Multiplication Table
Find K-th Smallest Pair Distance
參考資料:
https://leetcode.com/problems/k-th-smallest-prime-fraction/
https://leetcode.com/problems/k-th-smallest-prime-fraction/discuss/115531/C++-9lines-priority-queue