diff --git a/347. Top K Frequent Elements.md b/347. Top K Frequent Elements.md new file mode 100644 index 0000000..f637d6a --- /dev/null +++ b/347. Top K Frequent Elements.md @@ -0,0 +1,103 @@ +# [347. Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/description/) + +## Step1 +### 問題意図の考察 +- 整数配列numsと整数kが与えられ、numsの中で最頻出するk個の要素を取り出す。 +- 頻度集計、効率的な上位k要素を取り出す。 +- 問題文より、O(nlogn)より良い計算量を目指す。 + +### 解法を考える +- バケットソート + * 数を数える -> num frequency (unordered_map) + * 頻度ごとに箱へ格納 + * 高頻度側から取り出してk個集める + +```cpp +class Solution { + public: + std::vector topKFrequent(const std::vector& nums, int k) { + std::unordered_map freq; + freq.reserve(nums.size()); + for (int x : nums) { + ++freq[x]; + } + + std::vector> buckets(nums.size() + 1); + for (const auto& [val, f] : freq) { + buckets[f].push_back(val); + } + + std::vector answer; + answer.reserve(k); + for (int f = static_cast(buckets.size()) - 1; + f >= 1 && static_cast(answer.size()) < k; --f) { + for (int val : buckets[f]) { + answer.push_back(val); + if (static_cast(answer.size()) == k) break; + } + } + return answer; + } +}; + +``` + +## Step2 + +### コメント +- 解法がわからなかったので、参考のgithubより回答を確認。C++完走者をもう少し見つけたい。 + +- https://discord.com/channels/1084280443945353267/1367399154200088626/1371325723612151918 + *実際の仕事を連想する + +- std::nth_elements(線形時間) https://discord.com/channels/1084280443945353267/1183683738635346001/1185972070165782688 + * C. クイックセレクトについて + * 区間 [first, last) を完全には並べ替えず、イテレータ nth が指す要素を “ソート済みならそこに来る要素” にする。 +  comp(x, *nth) == true な要素は nth より前へ +  comp(*nth, x) == false な要素は nth 以降へ + +- ハッシュ連想配列 ref: https://en.cppreference.com/w/cpp/container/unordered_map.html?utm_source=chatgpt.com + * テンプレート仮引数の見方が、少しずつわかってきた。ここでは、reserve()に関して確認。要素と出現回数 + +- vector ref: https://en.cppreference.com/w/cpp/container/vector.html?utm_source=chatgpt.com + * 動的配列の性質(連続メモリ・再確保)、bucketsという命名は少し曖昧かな。ここでは、values_by_frequency とする。 + * freqもfrequencyへ変更。 + +- アルゴリズム戦略 実装までの余裕はなかったが、考えられる選択肢を整理。 + * A. バケットソート -> 高頻度側からk個集める。 + * B. 最小ヒープ(priority_queue) -> (freq, val) を最小ヒープに入れ、サイズが k を超えたら pop。 + * C. クイックセレクト(選択アルゴリズム) -> (val, freq) の配列(サイズ u)を作り、第 k 大の頻度を基準に分割(平均 O(u)) + * D. 周波数でソート -> vector> を freq 降順ソートして先頭 k 個。 + + +```cpp +class Solution { + public: + std::vector topKFrequent(const std::vector& nums, int k) { + std::unordered_map frequency; + frequency.reserve(nums.size()); + for (int x : nums) { + ++frequency[x]; + } + + std::vector> values_by_frequency(nums.size() + 1); + for (const auto& [val, f] : frequency) { + values_by_frequency[f].push_back(val); + } + + std::vector answer; + answer.reserve(k); + for (int f = static_cast(values_by_frequency.size()) - 1; + f >= 1 && static_cast(answer.size()) < k; --f) { + for (int val : values_by_frequency[f]) { + answer.push_back(val); + if (static_cast(answer.size()) == k) break; + } + } + return answer; + } +}; + +``` + +## Step3