diff --git a/arai60/find-k-pairs-with-smallest-sums/README.md b/arai60/find-k-pairs-with-smallest-sums/README.md new file mode 100644 index 0000000..b5d7076 --- /dev/null +++ b/arai60/find-k-pairs-with-smallest-sums/README.md @@ -0,0 +1,52 @@ +## 考察 +- 初見の問題 +- 方針を考える + - ソートする + - time: O(n^2 log n), space: O(n^2) + - n <= 10^5 なので、計算ステップ数は最悪10^10オーダー -> ✕ + - メモリも例えばintを10^10持つことを考えると、4 * 10^10 bytes = 40GB -> ✕ + - サイズkの最大ヒープを使う + - time: O(n^2 log k), space: O(k) + - spaceは大丈夫そうだが、timeがきつそう -> ✕ + - 最小ヒープを使う + - 各配列がソートされているのをうまく使いたい + - sumが要素の行列を考えて、行列の各行にnums1の要素を対応させると、行がソート済みの行列が見える + - この構造は何度か見たことがある + - merge k sorted lists の要領で小さいものから順番に取り出していけそう + - Ref. https://leetcode.com/problems/merge-k-sorted-lists/description/ + - メモリ上、あらかじめ全てのsumを計算しておくことができないので、その都度計算するように工夫する必要がありそう + - time: O(n log n + k log n), space: O(n) + - たぶんいける +- 最小ヒープを使う方針でやってみる +- あとは実装 + +## Step1 +- 上記のアルゴリズムを実装 +- time: O(n log n + k log n), space: O(n) +- パスはしたけど、かなり遅い(Runtime beats 5.05%) + +## Step2 +- Solutionsを覗いてみた +- サイズkの最大ヒープを使う O(n^2 * log k) のアルゴリズムを適切に枝狩りすることでもっと速度を出せるみたい +- この方針でもやってみる +- 実装してみて、結構自然な考え方かもしれないが、計算量評価が難しく感じた + +## Step3 +- Step1のアルゴリズムの方がしっくり来たため、そちらで実装した +- 1回目: 9m16s +- 2回目: 8m57s +- 3回目: 8m52s + +## Step4 +- 行列が縦方向にもソートされていたことに気づいたので、ロジックを少し修正 + - nums1.size() > kのとき、上からk行だけ考えればよかった + - k + 1行目より先に必ず上のk個が選ばれるため +- レビューを元に修正 +- 変数名の変更 + - pair -> elements (pairはstdの予約語のため) +- SumPairのelementsとindiceは要素数が必ず2つなので型を変更 + - vector -> pair +- SumPairにコンストラクタを定義して、同じ記述を集約 +- emplace_backやemplaceを使うことでコンストラクタの引数を直接受け取り、コンテナの末尾に新しい要素を直接構築できる +- moveすることで構造体のコピーを防ぎ、所有権だけ移動できる +- answerは要素数がkであることが確定しているので、先にメモリをreserveしておく diff --git a/arai60/find-k-pairs-with-smallest-sums/step1.cpp b/arai60/find-k-pairs-with-smallest-sums/step1.cpp new file mode 100644 index 0000000..cb253ce --- /dev/null +++ b/arai60/find-k-pairs-with-smallest-sums/step1.cpp @@ -0,0 +1,35 @@ +class Solution { +public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue min_sum_pair; + for (int i = 0; i < nums1.size(); ++i) { + int sum = nums1[i] + nums2[0]; + min_sum_pair.push({sum, {nums1[i], nums2[0]}, {i, 0}}); + } + + vector> answer; + while (answer.size() < k) { + SumPair sum_pair = min_sum_pair.top(); + min_sum_pair.pop(); + answer.push_back(sum_pair.pair); + + int index_nums1 = sum_pair.indice[0]; + int index_nums2 = sum_pair.indice[1]; + if (++index_nums2 >= nums2.size()) continue; + int sum = nums1[index_nums1] + nums2[index_nums2]; + min_sum_pair.push({sum, {nums1[index_nums1], nums2[index_nums2]}, {index_nums1, index_nums2}}); + } + return answer; + } + +private: + struct SumPair { + int sum; + vector pair; + vector indice; + + bool operator<(const SumPair& other) const { + return sum > other.sum; + } + }; +}; diff --git a/arai60/find-k-pairs-with-smallest-sums/step2.cpp b/arai60/find-k-pairs-with-smallest-sums/step2.cpp new file mode 100644 index 0000000..c9dc71c --- /dev/null +++ b/arai60/find-k-pairs-with-smallest-sums/step2.cpp @@ -0,0 +1,38 @@ +class Solution { +public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue min_sum_pairs; + int n = nums1.size(); + int m = nums2.size(); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < m; ++j) { + int sum = nums1[i] + nums2[j]; + if (min_sum_pairs.size() < k) { + min_sum_pairs.push({sum, {nums1[i], nums2[j]}}); + } else if (min_sum_pairs.size() == k && sum < min_sum_pairs.top().sum) { + min_sum_pairs.pop(); + min_sum_pairs.push({sum, {nums1[i], nums2[j]}}); + } else { + break; + } + } + } + + vector> answer; + while (!min_sum_pairs.empty()) { + answer.push_back(min_sum_pairs.top().pair); + min_sum_pairs.pop(); + } + return answer; + } + +private: + struct SumPair { + int sum; + vector pair; + + bool operator<(const SumPair& other) const { + return sum < other.sum; + } + }; +}; diff --git a/arai60/find-k-pairs-with-smallest-sums/step3.cpp b/arai60/find-k-pairs-with-smallest-sums/step3.cpp new file mode 100644 index 0000000..2b5c139 --- /dev/null +++ b/arai60/find-k-pairs-with-smallest-sums/step3.cpp @@ -0,0 +1,36 @@ +class Solution { +public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + vector sum_pairs; + for (int i = 0; i < nums1.size(); ++i) { + int sum = nums1[i] + nums2[0]; + sum_pairs.push_back({sum, {nums1[i], nums2[0]}, {i, 0}}); + } + + priority_queue min_sum_pairs(sum_pairs.begin(), sum_pairs.end()); + vector> answer; + while (answer.size() < k) { + SumPair sum_pair = min_sum_pairs.top(); + min_sum_pairs.pop(); + answer.push_back(sum_pair.pair); + + int index_nums1 = sum_pair.indice[0]; + int index_nums2 = sum_pair.indice[1]; + if (++index_nums2 >= nums2.size()) continue; + int sum = nums1[index_nums1] + nums2[index_nums2]; + min_sum_pairs.push({sum, {nums1[index_nums1], nums2[index_nums2]}, {index_nums1, index_nums2}}); + } + return answer; + } + +private: + struct SumPair { + int sum; + vector pair; + vector indice; + + bool operator<(const SumPair& other) const { + return sum > other.sum; + } + }; +}; diff --git a/arai60/find-k-pairs-with-smallest-sums/step4.cpp b/arai60/find-k-pairs-with-smallest-sums/step4.cpp new file mode 100644 index 0000000..a0171ca --- /dev/null +++ b/arai60/find-k-pairs-with-smallest-sums/step4.cpp @@ -0,0 +1,38 @@ +class Solution { +public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + vector sum_pairs; + for (int i = 0; i < nums1.size() && i < k; ++i) { + sum_pairs.emplace_back(nums1, nums2, i, 0); + } + + priority_queue min_sum_pairs(sum_pairs.begin(), sum_pairs.end()); + vector> answer; + answer.reserve(k); + while (answer.size() < k) { + SumPair sum_pair = move(min_sum_pairs.top()); + min_sum_pairs.pop(); + answer.push_back({sum_pair.elements.first, sum_pair.elements.second}); + + int index_nums1 = sum_pair.indice.first; + int index_nums2 = sum_pair.indice.second; + if (++index_nums2 >= nums2.size()) continue; + min_sum_pairs.emplace(nums1, nums2, index_nums1, index_nums2); + } + return answer; + } + +private: + struct SumPair { + int sum; + pair elements; + pair indice; + + SumPair(vector& nums1, vector& nums2, int index_nums1, int index_nums2) + : sum(nums1[index_nums1] + nums2[index_nums2]), elements({nums1[index_nums1], nums2[index_nums2]}), indice({index_nums1, index_nums2}) {} + + bool operator<(const SumPair& other) const { + return sum > other.sum; + } + }; +};