題目敘述

- 題目難度:
Medium
- 題目描述: 題目給定兩個陣列
nums1
以及 nums2
,兩者都以 non-decreasing order 排序,並且給定整數 k
,題目要求你從兩個陣列中個取出一個整數,定義成 pair (u, v)
請找出 k
具有最小總和的 pair u1, v1), (u2, v2), ..., (uk, vk)
解法
一開始的想法
一開始的想法比較暴力一點,一共三步:
- 兩個陣列都各自迭代找出所有 pair 組合
- 定義 minHeap 和 comparator 來比較pair之間誰的總和比較小
- pop k 次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution { public: vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) { vector<vector<int>> resultPairs; auto comparator = [](const pair<int, int> &leftPair, const pair<int, int> &rightPair){ int sumLeft = leftPair.first + leftPair.second; int sumRight = rightPair.first + rightPair.second; return sumLeft > sumRight; }; priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(comparator)> minHeap(comparator); for(int i=0; i<nums1.size();i++){ for(int j=0; j<nums2.size();j++){ minHeap.push({nums1[i],nums2[j]}); } }
while(!minHeap.empty() && k>0){ resultPairs.push_back({minHeap.top().first, minHeap.top().second}); minHeap.pop(); k--; }
return resultPairs; } };
|
這是原先寫的方法,但這種方法絕對爆炸,因為會找出 nums1.size() * nums2.size()
個pairs 記憶體一定炸開,會Memory Limit Exceeded
我的解法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution { public: vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) { vector<vector<int>> resultPairs;
auto comparator = [&](const pair<int, int> &leftPair, const pair<int, int> &rightPair){ long long sumLeft = (long long)(nums1[leftPair.first] + nums2[leftPair.second]); long long sumRight = (long long)(nums1[rightPair.first] + nums2[rightPair.second]); return sumLeft > sumRight; }; priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(comparator)> minHeap(comparator); int limit = min((int)nums1.size(), k); for(int i=0; i<limit; i++){ minHeap.push({i,0}); }
while(!minHeap.empty() && k-->0){ auto [i, j] = minHeap.top(); minHeap.pop(); resultPairs.push_back({nums1[i],nums2[j]}); if (j + 1 < (int)nums2.size()) { minHeap.push({i, j + 1}); } } return resultPairs; } };
|
上面這是後來改良的做法,大致流程一樣,但是這次不先算出所有 pairs,而是以隊伍的概念來進行:
- 每條隊伍只先派 第一個學生來參加比賽(即
(i,0)
)
- 用minHeap決定哪個學生的總分最小,就把他選出來
- 被選出來的隊伍 (推入
resultPairs
),才再派下一個學生 (i, j+1)
出來。
- 重複這個動作 k 次。
而這樣做的前提在於題目有說這兩個陣列都是排序好的,並且 heap 的comparator 會是以sum值作為比較基準,也就是說,每條隊伍都按照* *和大小**從小到大排好(因為 nums2
是排序的,所以 (i,0)
最小,(i,1)
第二小 …)
這樣改良的做法就可以降低heap的負擔,原先需要放 m*n個pair 現在只需要 min(k, nums1.size())
個元素
舉例
1 2 3
| nums1 = [1,7,11] nums2 = [2,4,6] k = 3
|
- 初始化: heap 裡有
(1,2)
、(7,2)
、(11,2)
- 第一次 pop →
(1,2)
→ 加入答案 → 推 (1,4)
- 第二次 pop →
(1,4)
→ 加入答案 → 推 (1,6)
- 第三次 pop →
(1,6)
→ 加入答案 → 推 (1, 沒有了)
執行結果

複雜度
時間複雜度
$O(klog min(k,∣nums1∣))$
空間複雜度
$O(k+min(k,∣nums1∣))$ = $O(k)$