From 79430e20497e3a012a0d1d0d231011479cf5d87e Mon Sep 17 00:00:00 2001 From: kazukiii Date: Fri, 14 Jun 2024 19:43:53 -0700 Subject: [PATCH 1/3] =?UTF-8?q?step1,=20step2,=20step3=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../find-k-pairs-with-smallest-sums/README.md | 38 +++++++++++++++++++ .../find-k-pairs-with-smallest-sums/step1.cpp | 35 +++++++++++++++++ .../find-k-pairs-with-smallest-sums/step2.cpp | 38 +++++++++++++++++++ .../find-k-pairs-with-smallest-sums/step3.cpp | 36 ++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 arai60/find-k-pairs-with-smallest-sums/README.md create mode 100644 arai60/find-k-pairs-with-smallest-sums/step1.cpp create mode 100644 arai60/find-k-pairs-with-smallest-sums/step2.cpp create mode 100644 arai60/find-k-pairs-with-smallest-sums/step3.cpp 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..75eabe7 --- /dev/null +++ b/arai60/find-k-pairs-with-smallest-sums/README.md @@ -0,0 +1,38 @@ +## 考察 +- 初見の問題 +- 方針を考える + - ソートする + - 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 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; + } + }; +}; From beec9a4213f5ebe5f06f8720da60aa964ccf505b Mon Sep 17 00:00:00 2001 From: kazukiii Date: Sat, 15 Jun 2024 20:10:00 -0700 Subject: [PATCH 2/3] =?UTF-8?q?step4=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../find-k-pairs-with-smallest-sums/README.md | 11 ++++++ .../find-k-pairs-with-smallest-sums/step4.cpp | 37 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 arai60/find-k-pairs-with-smallest-sums/step4.cpp diff --git a/arai60/find-k-pairs-with-smallest-sums/README.md b/arai60/find-k-pairs-with-smallest-sums/README.md index 75eabe7..2727e94 100644 --- a/arai60/find-k-pairs-with-smallest-sums/README.md +++ b/arai60/find-k-pairs-with-smallest-sums/README.md @@ -36,3 +36,14 @@ - 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にコンストラクタを定義して、同じ記述を集約 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..f31a045 --- /dev/null +++ b/arai60/find-k-pairs-with-smallest-sums/step4.cpp @@ -0,0 +1,37 @@ +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.push_back(SumPair(nums1, nums2, 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.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.push(SumPair(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; + } + }; +}; From 3eca6bde37598661bd1b56b44bd16992054b7e13 Mon Sep 17 00:00:00 2001 From: kazukiii Date: Mon, 17 Jun 2024 16:17:29 -0700 Subject: [PATCH 3/3] =?UTF-8?q?step4:=20=E4=B8=80=E9=83=A8=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E3=82=92=E8=A1=8C=E3=81=A3=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- arai60/find-k-pairs-with-smallest-sums/README.md | 3 +++ arai60/find-k-pairs-with-smallest-sums/step4.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arai60/find-k-pairs-with-smallest-sums/README.md b/arai60/find-k-pairs-with-smallest-sums/README.md index 2727e94..b5d7076 100644 --- a/arai60/find-k-pairs-with-smallest-sums/README.md +++ b/arai60/find-k-pairs-with-smallest-sums/README.md @@ -47,3 +47,6 @@ - SumPairのelementsとindiceは要素数が必ず2つなので型を変更 - vector -> pair - SumPairにコンストラクタを定義して、同じ記述を集約 +- emplace_backやemplaceを使うことでコンストラクタの引数を直接受け取り、コンテナの末尾に新しい要素を直接構築できる +- moveすることで構造体のコピーを防ぎ、所有権だけ移動できる +- answerは要素数がkであることが確定しているので、先にメモリをreserveしておく diff --git a/arai60/find-k-pairs-with-smallest-sums/step4.cpp b/arai60/find-k-pairs-with-smallest-sums/step4.cpp index f31a045..a0171ca 100644 --- a/arai60/find-k-pairs-with-smallest-sums/step4.cpp +++ b/arai60/find-k-pairs-with-smallest-sums/step4.cpp @@ -3,20 +3,21 @@ class Solution { vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { vector sum_pairs; for (int i = 0; i < nums1.size() && i < k; ++i) { - sum_pairs.push_back(SumPair(nums1, nums2, i, 0)); + 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 = min_sum_pairs.top(); + 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.push(SumPair(nums1, nums2, index_nums1, index_nums2)); + min_sum_pairs.emplace(nums1, nums2, index_nums1, index_nums2); } return answer; }