Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions 347-Top-K-Frequent-Elements/note.md

Copy link
Copy Markdown

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

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。参考にします。

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

時間計算量は O(n + k log n) のほうがより丁寧だと思いました.

n = k, n <= k などの場合でそれぞれ異なるように思います.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The 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回)
41 changes: 41 additions & 0 deletions 347-Top-K-Frequent-Elements/step1.py
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
20 changes: 20 additions & 0 deletions 347-Top-K-Frequent-Elements/step2.py
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
20 changes: 20 additions & 0 deletions 347-Top-K-Frequent-Elements/step3.py
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]] = {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

妙案が思い浮かばないんですが, counter という名前を少し変更したい気持ちになっています.
counterという名称から推測するよりも構造が複雑だったので.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かにそうですね
他の方にいただいたコメントのように、構造の方を単純にしてしまうというのが今回はいいのかもしれません:
#10 (comment)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

num_to_count_and_num という名前は思いついたのですが、野暮ったく感じました。構造のほうを単純にしてしまったほうが良いと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The 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())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

counter値: 出現回数で持っておいて、count_and_numを作るときに

count_and_num = [(count, num) for num, count in counter.items()]

と組にするのはどうでしょうか。
keyとvalueでnumが重複している冗長さが消えるので、個人的にはこちらが良いと感じます。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The 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