-
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,35 @@ | ||
| # 347. Top K Frequent Elements | ||
|
|
||
| <https://leetcode.com/problems/top-k-frequent-elements/> | ||
|
|
||
| ## step1(まず通す) | ||
|
|
||
| 値→回数でカウントした後に回数→値の変換が高速にできて回数の大小でトータルオーダーのものが欲しいと思って Frequency を定義したけど冷静に考えると [回数, 値]のリストで良かった(List の順序は辞書順で判定)。 | ||
|
|
||
| nums の長さを n としたとき、時間計算量は | ||
|
|
||
| - カウント部分が O(n) | ||
| - ヒープ化が O(n) | ||
| - 取り出すところが pop O(log n) をk 回で O(k log n) | ||
|
|
||
| なので O(k * log n) | ||
|
Comment on lines
+11
to
+15
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. 時間計算量は n = k, n <= k などの場合でそれぞれ異なるように思います.
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. ありがとうございます。特にkが小さい場合などはnの方が大きく効いてくるので、そちらが正しそうですね。 |
||
|
|
||
| 空間計算量は長さ n か k の配列(中身は固定サイズ)をいくつか持っているのとあとは定数だけなので O(n) | ||
|
|
||
| ## step2(整形&他の人のコードを読む) | ||
|
|
||
| ひとまずリストで書いてみた。 | ||
|
|
||
| - <https://github.com/potrue/leetcode/pull/9/changes> | ||
| - 自分が欲しかったものとして bidict というやつがあるらしい | ||
| - <https://pypi.org/project/bidict/> | ||
| - 自分の手でアルゴリズムを書かないとしたら Counter が便利 | ||
| - 逆引き辞書を `{回数: [その回数出てきた値のリスト]}` で持っておけばバケットソートできて速そう | ||
| - クイックセレクトという、クイックソートの類似のアルゴリズムがあるらしい | ||
| - ヒープじゃなくてソートして先頭k個を取ってくるなら、`{値: 回数}` 辞書のまま value でソートするという方法があるらしい | ||
| - <https://github.com/potrue/leetcode/pull/9/changes#r2083373096> | ||
| - heapq.heapify_max() などの max 系は 3.14 から使えるようになった新しい機能 | ||
| - <https://docs.python.org/ja/3.14/library/heapq.html> | ||
| - minheap しか使えないなら回数を符号反転で持つことで解決できる | ||
|
|
||
| ## step3(10分以内にさっとかける * 3回) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| from functools import total_ordering | ||
| from typing import Dict, List | ||
| import heapq | ||
|
|
||
|
|
||
| @total_ordering | ||
| class Frequency: | ||
| def __init__(self, value: int, count: int): | ||
| self.value = value | ||
| self.count = count | ||
|
|
||
| def __eq__(self, other: object) -> bool: | ||
| if not isinstance(other, self.__class__): | ||
| return NotImplemented | ||
| return self.count == other.count | ||
|
|
||
| def __lt__(self, other): | ||
| if not isinstance(other, self.__class__): | ||
| return NotImplemented | ||
| return self.count < other.count | ||
|
|
||
| def count_up(self): | ||
| self.count += 1 | ||
|
|
||
|
|
||
| class Solution: | ||
| def topKFrequent(self, nums: List[int], k: int) -> List[int]: | ||
| frequency_counter: Dict[int, Frequency] = {} | ||
| for num in nums: | ||
| if frequency_counter.get(num) is None: | ||
| frequency_counter[num] = Frequency(num, 1) | ||
| else: | ||
| frequency_counter[num].count_up() | ||
|
|
||
| frequency_counts = list(frequency_counter.values()) | ||
| heapq.heapify_max(frequency_counts) | ||
| k_most_frequent_elements = [] | ||
| while len(k_most_frequent_elements) < k: | ||
| k_most_frequent_elements.append(heapq.heappop_max(frequency_counts).value) | ||
|
|
||
| return k_most_frequent_elements |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| from typing import Dict, List | ||
| import heapq | ||
|
|
||
|
|
||
| class Solution: | ||
| def topKFrequent(self, nums: List[int], k: int) -> List[int]: | ||
| frequency_counter: Dict[int, List[int]] = {} | ||
| for num in nums: | ||
| if frequency_counter.get(num) is None: | ||
| frequency_counter[num] = [1, num] | ||
| else: | ||
| frequency_counter[num][0] += 1 | ||
|
|
||
| frequency_counts = list(frequency_counter.values()) | ||
| heapq.heapify_max(frequency_counts) | ||
| k_most_frequent_elements = [] | ||
| while len(k_most_frequent_elements) < k: | ||
| k_most_frequent_elements.append(heapq.heappop_max(frequency_counts)[1]) | ||
|
|
||
| return k_most_frequent_elements |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| from typing import Dict, List | ||
| import heapq | ||
|
|
||
|
|
||
| class Solution: | ||
| def topKFrequent(self, nums: List[int], k: int) -> List[int]: | ||
| # counter の value は [回数, 値]のリスト | ||
| counter: Dict[int, List[int]] = {} | ||
|
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. 確かにそうですね 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_to_count_and_num という名前は思いついたのですが、野暮ったく感じました。構造のほうを単純にしてしまったほうが良いと思います。
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 num in nums: | ||
| if counter.get(num) is None: | ||
| counter[num] = [1, num] | ||
| else: | ||
| counter[num][0] += 1 | ||
|
|
||
| count_and_num = list(counter.values()) | ||
|
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.
count_and_num = [(count, num) for num, count in counter.items()]と組にするのはどうでしょうか。
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. ありがとうございます。私も必要以上に複雑な構造になっていそうだったのが気になっていましたがそのように書くのがシンプルで良さそうです。 |
||
| heapq.heapify_max(count_and_num) | ||
| top_k = [] | ||
| while len(top_k) < k: | ||
| top_k.append(heapq.heappop_max(count_and_num)[1]) | ||
| return top_k | ||
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.
この方のPRも,いろんな解法のパターンが記載されていて,個人的には勉強になりましたので貼っておきます
https://github.com/dorxyxki/arai60/pull/9/changes
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.
ありがとうございます。参考にします。