-
Notifications
You must be signed in to change notification settings - Fork 0
347. Top K Frequent Elements #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| ## 考察 | ||
| - 過去に解いたことあり | ||
| - 方針 | ||
| - まずはカウントを行う | ||
| - time: O(n), space: O(n) | ||
| - カウントした結果を元にtopkを算出 | ||
| - 方針は3つ程思い浮かぶ | ||
| - ソート | ||
| - nums.length <= 10^5 なので、計数ソートが良さそう | ||
| - time: O(n), space: O(n) | ||
| - クイックセレクト | ||
| - ランダムにpivotを決める | ||
| - pivotの左側にpivot以下のもの、右側にpivotより大きいものを集める | ||
| - pivotが何番目かが確定するので、どちら側にk番目があるかは明白 | ||
| - これを繰り返せばいい | ||
| - any orderなので、pivotがk番目に来たら、pivotとその左側を返せばいい | ||
| - time: O(n), space: O(1) | ||
| - ヒープを使う | ||
| - サイズkの最小ヒープを用意して、サイズを保つように入れていく | ||
| - time: O(n log k), space: O(k) | ||
| - 全部さくっと実装できるようにしておきたい | ||
| - まずはソートでやる | ||
| - あとは実装 | ||
|
|
||
| ## Step1 | ||
| - 計数ソートを実装した | ||
| - time: O(n), space: O(n) | ||
|
|
||
| ## Step2 | ||
| - ヒープとクイックセレクトでも実装してみた | ||
| - 3通り実装してみて、計数ソートとクイックセレクトは脳の負荷が高い | ||
| - ヒープ使ってやるのが一番良さそう | ||
| - あとC++、エラー内容がPythonよりわかりにくく感じた(これは慣れかもしれない) | ||
|
|
||
| ## Step3 | ||
| - 1回目: 3m02s | ||
| - 2回目: 2m45s | ||
| - 3回目: 2m30s | ||
|
|
||
| ## Step4 | ||
| - レビューを元に修正 | ||
| - 変数名の見直し | ||
| - num_count -> num_to_count | ||
| - min_heap -> min_count_and_nums | ||
| - key, value -> num, count | ||
| - 拡張for文のコロンの前後にはspaceを入れる | ||
| - pairの代わりにstructを使うように修正 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| class Solution { | ||
| public: | ||
| vector<int> topKFrequent(vector<int>& nums, int k) { | ||
| int n = nums.size(); | ||
| unordered_map<int, int> num_count; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 何らかのマップであることを表すため、変数名に to をいれて、
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。見直してみます。 |
||
| for (int num: nums) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. num の後に空白を 1 つ空けましょう。 |
||
| num_count[num]++; | ||
| } | ||
|
|
||
| vector<int> buckets(n + 1, 0); | ||
| for (auto [key, value]: num_count) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。こちらも見直します。 |
||
| buckets[value]++; | ||
| } | ||
| for (int i = 0; i < n; ++i) { | ||
| buckets[i + 1] += buckets[i]; | ||
| } | ||
|
|
||
| vector<int> sorted(num_count.size()); | ||
| for (auto [key, value]: num_count) { | ||
| sorted[--buckets[value]] = key; | ||
| } | ||
|
|
||
| return vector<int>(sorted.end() - k, sorted.end()); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| class Solution { | ||
| public: | ||
| vector<int> topKFrequent(vector<int>& nums, int k) { | ||
| unordered_map<int, int> count; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. key と value にそれぞれ何が含まれるかを表す変数名にしたほうが良いと思います。 |
||
| for (int num: nums) { | ||
| count[num]++; | ||
| } | ||
|
|
||
| priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> min_heap; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pair を使用すると、中に何が入っているか想起しにくくなるので、やめたほうが良いと思います。 sturct を定義し、 また、 min_heap という名前は、「最小のヒープ」意味すると思うのですが、変数に含まれている値が何であるかを直接的に表現していないと思います。変数に含まれる値が何であるかを直接的に表現した英単語または英語句を付けることをお勧めします。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。pairの代わりに構造体を定義しようと思います。
たしかにheapであることは型見ればわかりますね。step4で参考にさせていただきます。 |
||
| for (auto [key, value]: count) { | ||
| min_heap.push({value, key}); | ||
| if (min_heap.size() > k) { | ||
| min_heap.pop(); | ||
| } | ||
| } | ||
|
|
||
| vector<int> answer; | ||
| while (!min_heap.empty()) { | ||
| answer.push_back(min_heap.top().second); | ||
| min_heap.pop(); | ||
| } | ||
| return answer; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| class Solution { | ||
| public: | ||
| vector<int> topKFrequent(vector<int>& nums, int k) { | ||
| unordered_map<int, int> count; | ||
| for (int num : nums) { | ||
| count[num]++; | ||
| } | ||
|
|
||
| vector<pair<int, int>> frequency_to_num; | ||
| for (auto& [key, value] : count) { | ||
| frequency_to_num.push_back({value, key}); | ||
| } | ||
|
|
||
| int n = frequency_to_num.size(); | ||
| quick_select(frequency_to_num, 0, n - 1, n - k); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 勉強と割り切って quick_select() を実装したのであればよいのですが、そうでなければ指し過ぎのように思います。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 問題文に |
||
| vector<int> answer; | ||
| for (int i = n - 1; i >= n - k; --i) { | ||
| answer.push_back(frequency_to_num[i].second); | ||
| } | ||
| return answer; | ||
| } | ||
|
|
||
| private: | ||
| void quick_select(vector<pair<int, int>>& array, int left, int right, int k) { | ||
| while (left < right) { | ||
| int pivot_index = partition(array, left, right); | ||
| if (pivot_index == k) return; | ||
| else if (pivot_index < k) left = pivot_index + 1; | ||
| else right = pivot_index - 1; | ||
| } | ||
| } | ||
|
|
||
| int partition(vector<pair<int, int>>& array, int left, int right) { | ||
| int pivot_index = rand_int(left, right); | ||
| int pivot_value = array[pivot_index].first; | ||
| swap(array[pivot_index], array[right]); | ||
| int store_index = left; | ||
| for (int i = left; i < right; ++i) { | ||
| if (array[i].first < pivot_value) { | ||
| swap(array[i], array[store_index]); | ||
| store_index++; | ||
| } | ||
| } | ||
| swap(array[store_index], array[right]); | ||
| return store_index; | ||
| } | ||
|
|
||
| int rand_int(int from, int to) { | ||
| return from + rand() % (to - from + 1); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| class Solution { | ||
| public: | ||
| vector<int> topKFrequent(vector<int>& nums, int k) { | ||
| unordered_map<int, int> num_count; | ||
| for (int num: nums) { | ||
| num_count[num]++; | ||
| } | ||
|
|
||
| priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> min_heap; | ||
| for (auto [key, value]: num_count) { | ||
| min_heap.push({value, key}); | ||
| if (min_heap.size() > k) { | ||
| min_heap.pop(); | ||
| } | ||
| } | ||
|
|
||
| vector<int> answer; | ||
| while (!min_heap.empty()) { | ||
| answer.push_back(min_heap.top().second); | ||
| min_heap.pop(); | ||
| } | ||
| return answer; | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| class Solution { | ||
| public: | ||
| vector<int> topKFrequent(vector<int>& nums, int k) { | ||
| unordered_map<int, int> num_to_count; | ||
| for (int num : nums) { | ||
| num_to_count[num]++; | ||
| } | ||
|
|
||
| priority_queue<NumCount> min_count_and_nums; | ||
| for (auto [num, count] : num_to_count) { | ||
| min_count_and_nums.push({count, num}); | ||
| if (min_count_and_nums.size() > k) { | ||
| min_count_and_nums.pop(); | ||
| } | ||
| } | ||
|
|
||
| vector<int> answer; | ||
| while (!min_count_and_nums.empty()) { | ||
| answer.push_back(min_count_and_nums.top().num); | ||
| min_count_and_nums.pop(); | ||
| } | ||
| return answer; | ||
| } | ||
|
|
||
| private: | ||
| struct NumCount { | ||
| int count; | ||
| int num; | ||
|
|
||
| bool operator<(const NumCount& other) const { | ||
| return count > other.count; | ||
| } | ||
| }; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
自分は計数ソートという単語を初めて聞きました。自分はソフトウェアエンジニアの常識には含まれていないと思うのですが、あまり自信がないです。 Computer Science Curricula 2023 には掲載されていませんでした。
nums.length <= 10^5 ですので、 O(n log n) のアルゴリズムでも十分で、そちらが想定解かもしれません。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
バケットソートですね。
思っているよりも標準ライブラリーは賢いことを色々しているので、思ったよりも標準ライブラリーのほうが速いんじゃないかなあと思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
常識の感覚大変参考になります。
たしかに標準ライブラリーは色々最適化されていることを考えると、初手では
std::sort()を使うようにしようと思います。