Skip to content

347. Top K Frequent Elements.md #9

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
113 changes: 113 additions & 0 deletions 347. Top K Frequent Elements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
URL: https://leetcode.com/problems/top-k-frequent-elements/description/

# Step 1

- 実装時間: 5分
- 時間計算量: O(n log n)
- 空間計算量: O(n)

kは条件で負の数は入ってこないので、バリデーションはしない。

```python
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count_frequency = {}
for num in nums:
if num in count_frequency:
count_frequency[num] += 1
else:
count_frequency[num] = 1
Comment on lines +16 to +19

Choose a reason for hiding this comment

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

こっちの書き方のほうが個人的には好きです。(が、趣味の範疇な気がします)

Suggested change
if num in count_frequency:
count_frequency[num] += 1
else:
count_frequency[num] = 1
if num not in count_frequency:
count_frequency[num] = 0
count_frequency[num] += 1

Choose a reason for hiding this comment

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

step2以降でお気づきですが、default_dictでこのあたりの初期化処理をなくせそうですね


top_k_elements = []
for key, value in sorted(count_frequency.items(), key=lambda x:x[1], reverse=True)[:k]:
top_k_elements.append(key)
Comment on lines +22 to +23

Choose a reason for hiding this comment

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

ループの対象がわかりづらいので、事前に別変数で定義したほうが可読性が上がりそうです。(frequency_ranking は ChatGPT が変数名案として返してきたものなんですが、意外とわかりやすいかもと思ってここで採用してみました)

また key, value は汎用的な用語なので、この問題に適した変数名にしたほうがよさそうです。(value は使っていないのでそもそも _ でよいですね)

Suggested change
for key, value in sorted(count_frequency.items(), key=lambda x:x[1], reverse=True)[:k]:
top_k_elements.append(key)
frequency_ranking = sorted(count_frequency.items(), key=lambda x:x[1], reverse=True)
for element, _ in frequency_ranking[:k]:
top_k_elements.append(element)


return top_k_elements
```

後半は1行でかけると気づいた。

```python
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count_frequency = {}
for num in nums:
if num in count_frequency:
count_frequency[num] += 1
else:
count_frequency[num] = 1
Comment on lines +35 to +38
Copy link

Choose a reason for hiding this comment

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

これも defaultdict 使わないとしてもいくつかあって、

if num not in count_frequency:
    count_frequency[num] = 0
count_frequency[num] += 1

count_frequency.setdefault(num, 0)
count_frequency[num] += 1

count_frequency[num] = count_frequency.get(num, 0) + 1

ですかね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。

以下自分メモ:
dictが持ってるメソッドを確認した。
https://docs.python.org/3/library/stdtypes.html#mapping-types-dict

Copy link

Choose a reason for hiding this comment

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

あ、そうそう。これを開いて読むの大事です。翻訳でもいいです。

return list(map(lambda x:x[0], sorted(count_frequency.items(), key=lambda x:x[1], reverse=True)[:k]))
Copy link

Choose a reason for hiding this comment

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

一行に詰め込み過ぎており、読むにあたり認知負荷が高く感じられました。

Copy link

Choose a reason for hiding this comment

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

fhiyo/leetcode#17 (comment)
7つあたりが限界に思いますね。
それに、これ、実行順序が、items, sorted, slice, map, list ですよね。目が左右に動きます。
sorted の結果を一回変数に置きたいです。

それはそうとして、文法への慣れの問題はあると思っています。いくつか案。

list(map(lambda x:x[0], items))

この形は

[k for k, _ in items]

のほうが見やすいでしょう。

また、sorted を使うならば、

sorted(count_frequency, key=count_frequency.get, reverse=True)[:k]

も手です。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。

文法への慣れの問題

すごくご指摘通りで、リストの内包表記が慣れないなと思ってるところでした。(書いてあったら読めるんですが、どうも自分で書く際の選択肢に出てこないです。。)
慣れ、ということでしばらく挑戦してみます!

```

# Step 2

- 参考にした
- https://github.com/tarinaihitori/leetcode/pull/9
- https://github.com/thonda28/leetcode/pull/17
- https://github.com/colorbox/leetcode/pull/24
- https://github.com/rihib/leetcode/pull/20
- https://github.com/hroc135/leetcode/pull/10
- https://github.com/hayashi-ay/leetcode/pull/60/
- https://github.com/kazukiii/leetcode/pull/10
- https://github.com/wf9a5m75/leetcode3/pull/3
- https://github.com/Yoshiki-Iwasa/Arai60/pull/8
- https://github.com/kagetora0924/leetcode-grind/pull/11
- https://github.com/seal-azarashi/leetcode/pull/9

- 変数の名前
- num_to_count のように、変数にどのような値が含まれているかを想像できるような変数名にしたほうが良い
- https://github.com/colorbox/leetcode/pull/24/files#r1740739103
- 変数名のnums_frequencyですが、整数に対して頻度なので、num_to_freqencyとかのほうが分かりやすい
- https://github.com/Ryotaro25/leetcode_first60/pull/10/files#r1623225196


- 別のパターン
- `dict()`のかわりに`defaultdict()`をつかう
- https://github.com/thonda28/leetcode/pull/17/files#r1769492893
- top kを選ぶときにheapqを使う。

Choose a reason for hiding this comment

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

優先度付きキューとかは割と見るイメージがあるので、(heapq ライブラリではなく)自分で一度実装してみるのもおもしろいと思います。

- バケットソートを使う
- https://github.com/kazukiii/leetcode/pull/10#discussion_r1639979474

- lambda関数を使ってる人がいなかった。
- 他の人のコードと見比べると、Step1では`x`が急に出てきているので、わかりにくくてよくない気がした。
- そもそも内包表記でかける
- `return [ num for count, num in top_k_frequent]`

- 問題設定は必ず k 種類あることになっていますが、なかった場合にどうするかは考えておいてください。
特別な値を返すか、Exception を投げるか、短いものでも返すか、プログラムを止めるか、そのあたりです。
- https://github.com/tarinaihitori/leetcode/pull/9/files#r1816996368

```python
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
num_to_count = defaultdict(int)
for num in nums:
num_to_count[num] += 1

top_k_frequent = []
for num, count in num_to_count.items():
heappush(top_k_frequent, (count, num))
if len(top_k_frequent) > k:
heappop(top_k_frequent)
return [num for count, num in top_k_frequent]
```

# Step 3

- 時間計算量: O(n log k)
- 空間計算量: O(n)

```python
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
num_to_count = defaultdict(int)
for num in nums:
num_to_count[num] += 1

top_k_frequent = []
for num, count in num_to_count.items():
heappush(top_k_frequent, (count, num))
if len(top_k_frequent) > k:
heappop(top_k_frequent)
return [num for _, num in top_k_frequent]
```