From af837a77e60183b3d687a649205aeb5df4d64b0b Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sat, 21 Feb 2026 10:32:54 +0900 Subject: [PATCH 01/11] step1 --- memo.md | 37 +++++++++++++++++++++++++++++++++++++ step1-1.py | 19 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 step1-1.py diff --git a/memo.md b/memo.md index 4bd0397..ef81f3b 100644 --- a/memo.md +++ b/memo.md @@ -1 +1,38 @@ # Step1 + +## アプローチ + +* mapに数字ごとの出現回数を記録. + * 全て記録し終わった後に出現回数の多い順に取り出し + * ordered_mapとかpythonにあるのかな? + * 自分の知っている知識でやるなら, heapにタプルで入れていく. 順番にk個取り出し +* 他の方法はパッと思いつかなかった. +* map -> heapの時間とか空間の見積もり + * 要素数をnとする + * O(n)で出現回数mapの構築 + * O(nlogn)で全ての出現回数と値のペアをheapに挿入. + * O(klogn)で上位k件を取り出し. + * 辞書の保存, heapともに, O(n) + + ## Code1-1 + + ```python + import heapq + class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + if k <= 0: + raise ValueError("k must be more than 0.") + value_to_count = {} + for num in nums: + if num not in value_to_count: + value_to_count[num] = 0 + value_to_count[num] += 1 + heap = [] + for value, count in value_to_count.items(): + heapq.heappush(heap, (-count, value)) + result = [] + for _ in range(k): + _, value = heapq.heappop(heap) + result.append(value) + return result + ``` \ No newline at end of file diff --git a/step1-1.py b/step1-1.py new file mode 100644 index 0000000..cd97e59 --- /dev/null +++ b/step1-1.py @@ -0,0 +1,19 @@ +import heapq +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + if k <= 0: + raise ValueError("k must be more than 0.") + value_to_count = {} + for num in nums: + if num not in value_to_count: + value_to_count[num] = 0 + value_to_count[num] += 1 + heap = [] + for value, count in value_to_count.items(): + heapq.heappush(heap, (-count, value)) + result = [] + for _ in range(k): + _, value = heapq.heappop(heap) + result.append(value) + return result + \ No newline at end of file From 445732ef9044a180b93940915df67173ebc97723 Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sat, 21 Feb 2026 11:15:03 +0900 Subject: [PATCH 02/11] step2 --- memo.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++- step2-1.py | 16 +++++++++++ step2-2.py | 9 ++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 step2-1.py create mode 100644 step2-2.py diff --git a/memo.md b/memo.md index ef81f3b..6bdefa2 100644 --- a/memo.md +++ b/memo.md @@ -35,4 +35,84 @@ _, value = heapq.heappop(heap) result.append(value) return result - ``` \ No newline at end of file + ``` + + # Step2 + + ## Code2-1 + + * 後から振り返り: heapに全件を入れる必要はなかった. 上位k件だけが入るように, 最初のk件以降は一番小さい値より小さいものは入れない. + + ```python + import heapq + class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + if k <= 0: + raise ValueError("k must be more than 0.") + value_to_count = {} + for num in nums: + value_to_count[num] = value_to_count.get(num, 0) + 1 + max_heap = [] + for value, count in value_to_count.items(): + heapq.heappush(max_heap, (-count, value)) + result = [] + for _ in range(k): + _, most_frequent_value = heapq.heappop(max_heap) + result.append(most_frequent_value) + return result + ``` + + ## Code2-2 + + * collections.Counterには標準で上位n件を表示するメソッドがある. + + ```python + from collections import Counter + + + class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + if k <= 0: + raise ValueError("k must be more than 0.") + num_counter = Counter(nums) + return [value for (value, count) in num_counter.most_common(k)] + ``` + + # 他の人の回答も見てみる + + ## 学んだこと + + * defaultdict + * 初めて遭遇したkeyに対しては, default_factoryが引数なしで呼ばれる. + * [ドキュメント](https://docs.python.org/3/library/collections.html#collections.defaultdict) + + * heapq.nlargest + * `sorted(iterable, key=key, reverse=True)[:n]`と同じ動作をする + * 内部的なアルゴリズムはもっと効率的. + * [公式実装](https://github.com/python/cpython/blob/06292614ff7cef0ba28da6dfded58fb0e731b2e3/Lib/heapq.py#L411) + + * Quick Select + * Quick Sortを途中までやるアルゴリズム + * [Discordの該当箇所](https://discord.com/channels/1084280443945353267/1183683738635346001/1185972070165782688) + + * 今回はたまたま大丈夫だったが, 同率k位がたくさんあるとどうなるかまで思考を巡らせられると良かった. + * https://github.com/potrue/leetcode/pull/9/files#r2083755650 + + * bucket sortの解法 + * https://github.com/potrue/leetcode/pull/9/files#diff-dce85bf5bc3acb0f755f06a75043875e90f52eadc5e761421acc856335cfec86R55 + * https://github.com/t-ooka/leetcode/commit/fbde086fff574ad5ff59eb6d39992a1de646c481 + + # 他の解法 + + ## Bucket Sort + + ```python + + ``` + + ## Quick Select + + ```python + + ``` + diff --git a/step2-1.py b/step2-1.py new file mode 100644 index 0000000..162acd7 --- /dev/null +++ b/step2-1.py @@ -0,0 +1,16 @@ +import heapq +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + if k <= 0: + raise ValueError("k must be more than 0.") + value_to_count = {} + for num in nums: + value_to_count[num] = value_to_count.get(num, 0) + 1 + max_heap = [] + for value, count in value_to_count.items(): + heapq.heappush(max_heap, (-count, value)) + result = [] + for _ in range(k): + _, most_frequent_value = heapq.heappop(max_heap) + result.append(most_frequent_value) + return result \ No newline at end of file diff --git a/step2-2.py b/step2-2.py new file mode 100644 index 0000000..d07b3d2 --- /dev/null +++ b/step2-2.py @@ -0,0 +1,9 @@ +from collections import Counter + + +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + if k <= 0: + raise ValueError("k must be more than 0.") + num_counter = Counter(nums) + return [value for (value, count) in num_counter.most_common(k)] \ No newline at end of file From 0b4b6d4dee2ada5e7a67e55634f904927949133c Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sat, 21 Feb 2026 11:16:35 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- memo.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/memo.md b/memo.md index 6bdefa2..34bacdd 100644 --- a/memo.md +++ b/memo.md @@ -7,12 +7,14 @@ * ordered_mapとかpythonにあるのかな? * 自分の知っている知識でやるなら, heapにタプルで入れていく. 順番にk個取り出し * 他の方法はパッと思いつかなかった. + * 追記: bucket sortとquick selectを思いつけると良かった. * map -> heapの時間とか空間の見積もり * 要素数をnとする * O(n)で出現回数mapの構築 * O(nlogn)で全ての出現回数と値のペアをheapに挿入. * O(klogn)で上位k件を取り出し. * 辞書の保存, heapともに, O(n) + * 追記: heapには常にk件だけが保存されるようにしたら, heapの使用する外部メモリはO(k)に収まる. ## Code1-1 From 1277691410ec9bd497633595ddbe28f1b9cbec32 Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sat, 21 Feb 2026 11:24:24 +0900 Subject: [PATCH 04/11] minor --- memo.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/memo.md b/memo.md index 34bacdd..aacd21b 100644 --- a/memo.md +++ b/memo.md @@ -104,6 +104,10 @@ * https://github.com/potrue/leetcode/pull/9/files#diff-dce85bf5bc3acb0f755f06a75043875e90f52eadc5e761421acc856335cfec86R55 * https://github.com/t-ooka/leetcode/commit/fbde086fff574ad5ff59eb6d39992a1de646c481 + * 標準の辞書の順番 + * [公式ドキュメント](https://docs.python.org/3/library/stdtypes.html#dict:~:text=Dictionaries%20preserve%20insertion%20order.%20Note%20that%20updating%20a%20key%20does%20not%20affect%20the%20order.%20Keys%20added%20after%20deletion%20are%20inserted%20at%20the%20end.) + * > Dictionaries preserve insertion order. Note that updating a key does not affect the order. Keys added after deletion are inserted at the end. + # 他の解法 ## Bucket Sort @@ -117,4 +121,12 @@ ```python ``` + + # おまけ + + ## Quick Sort + + ```python + + ``` From 45c5fdcc7d644bc2e4c4e11128854561338e18cf Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sat, 21 Feb 2026 15:57:38 +0900 Subject: [PATCH 05/11] quick select --- bucket_sort.py | 0 quick_select.py | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ quick_sort.py | 0 3 files changed, 113 insertions(+) create mode 100644 bucket_sort.py create mode 100644 quick_select.py create mode 100644 quick_sort.py diff --git a/bucket_sort.py b/bucket_sort.py new file mode 100644 index 0000000..e69de29 diff --git a/quick_select.py b/quick_select.py new file mode 100644 index 0000000..c1cb891 --- /dev/null +++ b/quick_select.py @@ -0,0 +1,113 @@ +import copy +import random +from enum import Enum + + +class PivotMethod(Enum): + LAST_ELEMENT = 0 + RANDOM_ELEMENT = 1 + MEDIAN_OF_MEDIANS = 2 + +class PartitionMethod(Enum): + LOMUTO = 0 + HOARE = 1 + +def get_pivot_by_last_element(nums): + return nums[-1] + +def get_pivot_by_random_element(nums): + random_idx = random.choice(range(len(nums))) + nums[random_idx], nums[-1] = nums[-1], nums[random_idx] + return nums[-1] + + +# Wikipedia URL +# https://en.wikipedia.org/wiki/Quicksort#Lomuto_partition_scheme +def partition_by_lomuto(arg_nums, pivot_func): + nums = copy.deepcopy(arg_nums) + pivot = pivot_func(nums) + # print("pivot", pivot) + n = len(nums) + idx_to_exchange = 0 + for i in range(n - 1): + if nums[i] <= pivot: + nums[i], nums[idx_to_exchange] = nums[idx_to_exchange], nums[i] + idx_to_exchange += 1 + nums[idx_to_exchange], nums[n - 1] = nums[n - 1], nums[idx_to_exchange] + return idx_to_exchange, nums + + +# Wikipedia URL +# https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme +def partition_by_hoare(arg_nums, pivot_func): + nums = copy.deepcopy(arg_nums) + pivot = pivot_func(nums) + # print("pivot", pivot) + n = len(nums) + less_than_or_eq_pivot = 0 + more_than_pivot = n - 1 + while True: + # TODO: pivot以下の要素だけで構成されているとOutOfIndexになる + while nums[less_than_or_eq_pivot] <= pivot: + less_than_or_eq_pivot += 1 + # TODO: pivotより大きい要素だけで構成されていると無限ループになる + while nums[more_than_pivot] > pivot: + more_than_pivot -= 1 + if more_than_pivot < less_than_or_eq_pivot: + return more_than_pivot, nums + nums[less_than_or_eq_pivot], nums[more_than_pivot] = nums[more_than_pivot], nums[less_than_or_eq_pivot] + + +def partition(nums, partition_method, pivot_method): + pivot_func = None + if pivot_method == PivotMethod.LAST_ELEMENT: + pivot_func = get_pivot_by_last_element + elif pivot_method == PivotMethod.RANDOM_ELEMENT: + pivot_func = get_pivot_by_random_element + else: + raise ValueError("pivot method must be the value of PivotMethod(Enum)") + + if partition_method == PartitionMethod.HOARE: + return partition_by_hoare(nums, pivot_func) + elif partition_method == PartitionMethod.LOMUTO: + return partition_by_lomuto(nums, pivot_func) + else: + raise ValueError("partition method must be the value of PartitionMethod(Enum)") + + + +def test_partition(nums): + idx, partitioned_nums = partition(nums, PartitionMethod.HOARE, PivotMethod.LAST_ELEMENT) + print(partitioned_nums, idx) + idx, partitioned_nums = partition(nums, PartitionMethod.LOMUTO, PivotMethod.LAST_ELEMENT) + print(partitioned_nums, idx) + idx, partitioned_nums = partition(nums, PartitionMethod.HOARE, PivotMethod.RANDOM_ELEMENT) + print(partitioned_nums, idx) + idx, partitioned_nums = partition(nums, PartitionMethod.LOMUTO, PivotMethod.RANDOM_ELEMENT) + print(partitioned_nums, idx) + """ + pivot 5 + [1, 3, 5, 51] 2 + pivot 5 + [1, 3, 5, 51] 2 + pivot 3 + [1, 3, 51, 5] 1 + pivot 51 + [1, 3, 5, 51] 3 + """ + +def quick_select(nums, k): + print(nums, k, "search") + partition_idx, partitioned_nums = partition(nums, PartitionMethod.LOMUTO, PivotMethod.LAST_ELEMENT) + print(partitioned_nums, partition_idx) + num_elements_lte_pivot = partition_idx + 1 + # hoare使ったらk番目とは限らなくなるのでは?? + if k == num_elements_lte_pivot: + return partitioned_nums[partition_idx] + + if num_elements_lte_pivot > k: + return quick_select(partitioned_nums[:partition_idx], k) + + return quick_select(partitioned_nums[partition_idx + 1:], k - num_elements_lte_pivot) + +print(quick_select([1,5,3,9,2], 3)) \ No newline at end of file diff --git a/quick_sort.py b/quick_sort.py new file mode 100644 index 0000000..e69de29 From b78322aaee5a6356f3fd4c820a31f94befb7222e Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sat, 21 Feb 2026 16:59:39 +0900 Subject: [PATCH 06/11] md --- bucket_sort.py | 0 memo.md | 145 ++++++++++++++++++++++++++++++++++++++++-------- quick_select.py | 64 ++++++++++++++++----- quick_sort.py | 0 4 files changed, 174 insertions(+), 35 deletions(-) delete mode 100644 bucket_sort.py delete mode 100644 quick_sort.py diff --git a/bucket_sort.py b/bucket_sort.py deleted file mode 100644 index e69de29..0000000 diff --git a/memo.md b/memo.md index aacd21b..351fe85 100644 --- a/memo.md +++ b/memo.md @@ -108,25 +108,126 @@ * [公式ドキュメント](https://docs.python.org/3/library/stdtypes.html#dict:~:text=Dictionaries%20preserve%20insertion%20order.%20Note%20that%20updating%20a%20key%20does%20not%20affect%20the%20order.%20Keys%20added%20after%20deletion%20are%20inserted%20at%20the%20end.) * > Dictionaries preserve insertion order. Note that updating a key does not affect the order. Keys added after deletion are inserted at the end. - # 他の解法 - - ## Bucket Sort - - ```python - - ``` - - ## Quick Select - - ```python - - ``` - - # おまけ - - ## Quick Sort - - ```python - - ``` - +## Quick Selectの実装(今回の問題の解法ではなく, k番目に小さい値を返す) + +* partitionの方法をLomutoにしないと, 正しい動作はしない + +```python +import copy +import random +from enum import Enum + + +class PivotMethod(Enum): + LAST_ELEMENT = 0 + RANDOM_ELEMENT = 1 + MEDIAN_OF_MEDIANS = 2 + +class PartitionMethod(Enum): + LOMUTO = 0 + HOARE = 1 + +def get_pivot_by_last_element(nums): + return nums[-1] + +def get_pivot_by_random_element(nums): + random_idx = random.choice(range(len(nums))) + nums[random_idx], nums[-1] = nums[-1], nums[random_idx] + return nums[-1] + + +# Wikipedia URL +# https://en.wikipedia.org/wiki/Quicksort#Lomuto_partition_scheme +def partition_by_lomuto(arg_nums, pivot_func): + nums = copy.deepcopy(arg_nums) + pivot = pivot_func(nums) + # print("pivot", pivot) + n = len(nums) + idx_to_exchange = 0 + for i in range(n - 1): + if nums[i] <= pivot: + nums[i], nums[idx_to_exchange] = nums[idx_to_exchange], nums[i] + idx_to_exchange += 1 + nums[idx_to_exchange], nums[n - 1] = nums[n - 1], nums[idx_to_exchange] + return idx_to_exchange, nums + + +# Wikipedia URL +# https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme +def partition_by_hoare(arg_nums, pivot_func): + nums = copy.deepcopy(arg_nums) + pivot = pivot_func(nums) + print("pivot", pivot) + n = len(nums) + less_than_or_eq_pivot = 0 + more_than_pivot = n - 1 + while True: + while less_than_or_eq_pivot < n and nums[less_than_or_eq_pivot] <= pivot: + less_than_or_eq_pivot += 1 + if less_than_or_eq_pivot == n: + return n - 1, nums + while more_than_pivot >= 0 and nums[more_than_pivot] > pivot: + more_than_pivot -= 1 + if more_than_pivot == -1: + return 0, nums + if more_than_pivot < less_than_or_eq_pivot: + print("returning", more_than_pivot, nums) + return more_than_pivot, nums + print("swap", less_than_or_eq_pivot, more_than_pivot) + nums[less_than_or_eq_pivot], nums[more_than_pivot] = nums[more_than_pivot], nums[less_than_or_eq_pivot] + + +def partition(nums, partition_method, pivot_method): + pivot_func = None + if pivot_method == PivotMethod.LAST_ELEMENT: + pivot_func = get_pivot_by_last_element + elif pivot_method == PivotMethod.RANDOM_ELEMENT: + pivot_func = get_pivot_by_random_element + else: + raise ValueError("pivot method must be the value of PivotMethod(Enum)") + + if partition_method == PartitionMethod.HOARE: + return partition_by_hoare(nums, pivot_func) + elif partition_method == PartitionMethod.LOMUTO: + return partition_by_lomuto(nums, pivot_func) + else: + raise ValueError("partition method must be the value of PartitionMethod(Enum)") + + +def quick_select(nums, k, partition_method): + if partition_method == PartitionMethod.HOARE: + partition_idx, partitioned_nums = partition(nums, PartitionMethod.HOARE, PivotMethod.LAST_ELEMENT) + else: + partition_idx, partitioned_nums = partition(nums, PartitionMethod.LOMUTO, PivotMethod.LAST_ELEMENT) + num_elements_lte_pivot = partition_idx + 1 + if k == num_elements_lte_pivot: + # ここはhoareだと正しく動作しない. + # lomutoはpartition_idxに必ずpivotとなった値が存在するが, hoareでは何が存在するか不明 + # nums = [4, 10, 1, 2, 7] k = 4をhoareで動作することを考える + # pivotでいちばんうしろの7を選択. + # 4 10 1 2 7 + # l r + # 4 10 1 2 7 + # l r + # swap!!! + # 4 7 1 2 10 + # l r + # 4 7 1 2 10 + # l r + # 4 7 1 2 10 + # l r + # 4 7 1 2 10 + # lr + # 4 7 1 2 10 + # r l + # return the position of r(=3) + # num_elements_lte_pivot = 3 + 1 = 4 + # これはkに等しいのでpartitioned_nums[3]を返す + # しかしこれの値は2であり, 正しい値の7とは異なる + return partitioned_nums[partition_idx] + + if num_elements_lte_pivot > k: + return quick_select(partitioned_nums[:partition_idx], k, partition_method) + + return quick_select(partitioned_nums[partition_idx + 1:], k - num_elements_lte_pivot, partition_method) +``` \ No newline at end of file diff --git a/quick_select.py b/quick_select.py index c1cb891..95f4840 100644 --- a/quick_select.py +++ b/quick_select.py @@ -42,19 +42,23 @@ def partition_by_lomuto(arg_nums, pivot_func): def partition_by_hoare(arg_nums, pivot_func): nums = copy.deepcopy(arg_nums) pivot = pivot_func(nums) - # print("pivot", pivot) + print("pivot", pivot) n = len(nums) less_than_or_eq_pivot = 0 more_than_pivot = n - 1 while True: - # TODO: pivot以下の要素だけで構成されているとOutOfIndexになる - while nums[less_than_or_eq_pivot] <= pivot: + while less_than_or_eq_pivot < n and nums[less_than_or_eq_pivot] <= pivot: less_than_or_eq_pivot += 1 - # TODO: pivotより大きい要素だけで構成されていると無限ループになる - while nums[more_than_pivot] > pivot: + if less_than_or_eq_pivot == n: + return n - 1, nums + while more_than_pivot >= 0 and nums[more_than_pivot] > pivot: more_than_pivot -= 1 + if more_than_pivot == -1: + return 0, nums if more_than_pivot < less_than_or_eq_pivot: + print("returning", more_than_pivot, nums) return more_than_pivot, nums + print("swap", less_than_or_eq_pivot, more_than_pivot) nums[less_than_or_eq_pivot], nums[more_than_pivot] = nums[more_than_pivot], nums[less_than_or_eq_pivot] @@ -96,18 +100,52 @@ def test_partition(nums): [1, 3, 5, 51] 3 """ -def quick_select(nums, k): - print(nums, k, "search") - partition_idx, partitioned_nums = partition(nums, PartitionMethod.LOMUTO, PivotMethod.LAST_ELEMENT) - print(partitioned_nums, partition_idx) +def quick_select(nums, k, partition_method): + if partition_method == PartitionMethod.HOARE: + partition_idx, partitioned_nums = partition(nums, PartitionMethod.HOARE, PivotMethod.LAST_ELEMENT) + else: + partition_idx, partitioned_nums = partition(nums, PartitionMethod.LOMUTO, PivotMethod.LAST_ELEMENT) num_elements_lte_pivot = partition_idx + 1 - # hoare使ったらk番目とは限らなくなるのでは?? if k == num_elements_lte_pivot: + # ここはhoareだと正しく動作しない. + # lomutoはpartition_idxに必ずpivotとなった値が存在するが, hoareでは何が存在するか不明 + # nums = [4, 10, 1, 2, 7] k = 4をhoareで動作することを考える + # pivotでいちばんうしろの7を選択. + # 4 10 1 2 7 + # l r + # 4 10 1 2 7 + # l r + # swap!!! + # 4 7 1 2 10 + # l r + # 4 7 1 2 10 + # l r + # 4 7 1 2 10 + # l r + # 4 7 1 2 10 + # lr + # 4 7 1 2 10 + # r l + # return the position of r(=3) + # num_elements_lte_pivot = 3 + 1 = 4 + # これはkに等しいのでpartitioned_nums[3]を返す + # しかしこれの値は2であり, 正しい値の7とは異なる return partitioned_nums[partition_idx] if num_elements_lte_pivot > k: - return quick_select(partitioned_nums[:partition_idx], k) + return quick_select(partitioned_nums[:partition_idx], k, partition_method) - return quick_select(partitioned_nums[partition_idx + 1:], k - num_elements_lte_pivot) + return quick_select(partitioned_nums[partition_idx + 1:], k - num_elements_lte_pivot, partition_method) + +def test_quick_select(): + num_iteration = 100 + for _ in range(num_iteration): + nums = [random.randint(0, 10) for _ in range(5)] + k = random.randint(0, 4) + hoare_result = quick_select(nums, k, PartitionMethod.HOARE) + lomuto_result = quick_select(nums, k, PartitionMethod.LOMUTO) + gt = sorted(nums)[k - 1] + assert hoare_result == lomuto_result, f"hoare {hoare_result}, lomuto: {lomuto_result}. nums: {nums}, k: {k}, gt: {gt}" + -print(quick_select([1,5,3,9,2], 3)) \ No newline at end of file +print(quick_select([4, 10, 1, 2, 7], 4, PartitionMethod.HOARE)) \ No newline at end of file diff --git a/quick_sort.py b/quick_sort.py deleted file mode 100644 index e69de29..0000000 From f432544967ac93155636ffee53443d51b75f3daa Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sat, 21 Feb 2026 18:37:10 +0900 Subject: [PATCH 07/11] quick select and bubble sort --- memo.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ step2-3.py | 21 ++++++++++++++++ step2-4.py | 38 +++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 step2-3.py create mode 100644 step2-4.py diff --git a/memo.md b/memo.md index 351fe85..2ed6f45 100644 --- a/memo.md +++ b/memo.md @@ -230,4 +230,74 @@ def quick_select(nums, k, partition_method): return quick_select(partitioned_nums[:partition_idx], k, partition_method) return quick_select(partitioned_nums[partition_idx + 1:], k - num_elements_lte_pivot, partition_method) +``` + +# 別の解法を実装 + +## Bucket Sort + +```python +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + max_count = 0 + num_to_count = {} + for num in nums: + num_to_count.setdefault(num, 0) + num_to_count[num] += 1 + max_count = max(max_count, num_to_count[num]) + nums_by_count = [[] for _ in range(max_count + 1)] + for num, count in num_to_count.items(): + nums_by_count[count].append(num) + result = [] + for count in range(max_count, -1, -1): + if len(nums_by_count[count]) + len(result) <= k: + result.extend(nums_by_count[count]) + continue + num_elements_to_add = k - len(result) + result.extend(nums_by_count[count][:num_elements_to_add]) + break + return result +``` + +## Quick Select + +```python +# Quick Select +import random + + +class Solution: + def partition(self, unique_nums, num_to_counts, left, right, pivot_idx): + unique_nums[right], unique_nums[pivot_idx] = unique_nums[pivot_idx], unique_nums[right] + pivot = num_to_counts[unique_nums[right]] + partition_idx = left + for i in range(left, right): + if num_to_counts[unique_nums[i]] <= pivot: + unique_nums[i], unique_nums[partition_idx] = unique_nums[partition_idx], unique_nums[i] + partition_idx += 1 + unique_nums[partition_idx], unique_nums[right] = unique_nums[right], unique_nums[partition_idx] + return partition_idx + + def quick_select(self, unique_nums, num_to_counts, left, right, smallest_k): + if left == right: + return + pivot_idx = random.randint(left, right) + partition_idx = self.partition(unique_nums, num_to_counts, left, right, pivot_idx) + if partition_idx == smallest_k: + return + if partition_idx > smallest_k: + self.quick_select(unique_nums, num_to_counts, left, partition_idx - 1, smallest_k) + else: + self.quick_select(unique_nums, num_to_counts, partition_idx + 1, right, smallest_k) + return + + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + num_to_counts = {} + for num in nums: + num_to_counts.setdefault(num, 0) + num_to_counts[num] += 1 + unique_nums = list(num_to_counts) + n = len(unique_nums) + self.quick_select(unique_nums, num_to_counts, 0, n - 1, n - k) + return unique_nums[n - k:] ``` \ No newline at end of file diff --git a/step2-3.py b/step2-3.py new file mode 100644 index 0000000..5345d06 --- /dev/null +++ b/step2-3.py @@ -0,0 +1,21 @@ +# Bucket Sort +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + max_count = 0 + num_to_count = {} + for num in nums: + num_to_count.setdefault(num, 0) + num_to_count[num] += 1 + max_count = max(max_count, num_to_count[num]) + nums_by_count = [[] for _ in range(max_count + 1)] + for num, count in num_to_count.items(): + nums_by_count[count].append(num) + result = [] + for count in range(max_count, -1, -1): + if len(nums_by_count[count]) + len(result) <= k: + result.extend(nums_by_count[count]) + continue + num_elements_to_add = k - len(result) + result.extend(nums_by_count[count][:num_elements_to_add]) + break + return result \ No newline at end of file diff --git a/step2-4.py b/step2-4.py new file mode 100644 index 0000000..99300c7 --- /dev/null +++ b/step2-4.py @@ -0,0 +1,38 @@ +# Quick Select +import random + + +class Solution: + def partition(self, unique_nums, num_to_counts, left, right, pivot_idx): + unique_nums[right], unique_nums[pivot_idx] = unique_nums[pivot_idx], unique_nums[right] + pivot = num_to_counts[unique_nums[right]] + partition_idx = left + for i in range(left, right): + if num_to_counts[unique_nums[i]] <= pivot: + unique_nums[i], unique_nums[partition_idx] = unique_nums[partition_idx], unique_nums[i] + partition_idx += 1 + unique_nums[partition_idx], unique_nums[right] = unique_nums[right], unique_nums[partition_idx] + return partition_idx + + def quick_select(self, unique_nums, num_to_counts, left, right, smallest_k): + if left == right: + return + pivot_idx = random.randint(left, right) + partition_idx = self.partition(unique_nums, num_to_counts, left, right, pivot_idx) + if partition_idx == smallest_k: + return + if partition_idx > smallest_k: + self.quick_select(unique_nums, num_to_counts, left, partition_idx - 1, smallest_k) + else: + self.quick_select(unique_nums, num_to_counts, partition_idx + 1, right, smallest_k) + return + + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + num_to_counts = {} + for num in nums: + num_to_counts.setdefault(num, 0) + num_to_counts[num] += 1 + unique_nums = list(num_to_counts) + n = len(unique_nums) + self.quick_select(unique_nums, num_to_counts, 0, n - 1, n - k) + return unique_nums[n - k:] \ No newline at end of file From 1ac1d26c35638cdcc832a3266e45bc32c376a7ce Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sun, 22 Feb 2026 15:51:20 +0900 Subject: [PATCH 08/11] =?UTF-8?q?step3=201=E5=9B=9E=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- step3-1.py | 18 ++++++++++++++++++ step3-2.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 step3-1.py create mode 100644 step3-2.py diff --git a/step3-1.py b/step3-1.py new file mode 100644 index 0000000..a908544 --- /dev/null +++ b/step3-1.py @@ -0,0 +1,18 @@ +# Heapを使用 +import heapq + +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + num_to_count = {} + for num in nums: + num_to_count.setdefault(num, 0) + num_to_count[num] += 1 + topk_heap = [] + for num, count in num_to_count.items(): + if len(topk_heap) < k: + heapq.heappush(topk_heap, (count, num)) + continue + if count <= topk_heap[0][0]: + continue + heapq.heappushpop(topk_heap, (count, num)) + return [num for count ,num in topk_heap] diff --git a/step3-2.py b/step3-2.py new file mode 100644 index 0000000..9bbc481 --- /dev/null +++ b/step3-2.py @@ -0,0 +1,39 @@ +# QuickSelectを使用 +import random + + +class Solution: + def partition(self, uniques, num_to_count, left, right, pivot_idx): + pivot = num_to_count[uniques[pivot_idx]] + uniques[pivot_idx], uniques[right] = uniques[right], uniques[pivot_idx] + partition_idx = left + for i in range(left, right): + if num_to_count[uniques[i]] <= pivot: + uniques[i], uniques[partition_idx] = uniques[partition_idx], uniques[i] + partition_idx += 1 + uniques[partition_idx], uniques[right] = uniques[right], uniques[partition_idx] + return partition_idx + + def quick_select(self, uniques, num_to_count, left, right, smallest_k): + if left == right: + return + pivot_idx = random.randint(left, right) + partitioned_idx = self.partition(uniques, num_to_count, left, right, pivot_idx) + if partitioned_idx == smallest_k: + return + if partitioned_idx > smallest_k: + self.quick_select(uniques, num_to_count, left, partitioned_idx - 1, smallest_k) + return + self.quick_select(uniques, num_to_count, partitioned_idx + 1, right, smallest_k) + return + + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + num_to_count = {} + for num in nums: + num_to_count.setdefault(num, 0) + num_to_count[num] += 1 + n = len(num_to_count) + uniques = list(num_to_count) + self.quick_select(uniques, num_to_count, 0, n - 1, n - k) + return uniques[n - k:] + \ No newline at end of file From 02f805ec2c3809be1823e1397f4843cd54d8a9fa Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sun, 22 Feb 2026 17:50:23 +0900 Subject: [PATCH 09/11] =?UTF-8?q?step3=20=EF=BC=92=E5=9B=9E=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- step3-1.py | 10 +++++----- step3-2.py | 26 +++++++++++++------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/step3-1.py b/step3-1.py index a908544..88802c0 100644 --- a/step3-1.py +++ b/step3-1.py @@ -3,16 +3,16 @@ class Solution: def topKFrequent(self, nums: List[int], k: int) -> List[int]: - num_to_count = {} + num_to_counts = {} for num in nums: - num_to_count.setdefault(num, 0) - num_to_count[num] += 1 + num_to_counts.setdefault(num, 0) + num_to_counts[num] += 1 topk_heap = [] - for num, count in num_to_count.items(): + for num, count in num_to_counts.items(): if len(topk_heap) < k: heapq.heappush(topk_heap, (count, num)) continue if count <= topk_heap[0][0]: continue heapq.heappushpop(topk_heap, (count, num)) - return [num for count ,num in topk_heap] + return [num for _, num in topk_heap] \ No newline at end of file diff --git a/step3-2.py b/step3-2.py index 9bbc481..804eabc 100644 --- a/step3-2.py +++ b/step3-2.py @@ -1,18 +1,17 @@ # QuickSelectを使用 import random - class Solution: def partition(self, uniques, num_to_count, left, right, pivot_idx): pivot = num_to_count[uniques[pivot_idx]] uniques[pivot_idx], uniques[right] = uniques[right], uniques[pivot_idx] - partition_idx = left + partitioned_idx = left for i in range(left, right): if num_to_count[uniques[i]] <= pivot: - uniques[i], uniques[partition_idx] = uniques[partition_idx], uniques[i] - partition_idx += 1 - uniques[partition_idx], uniques[right] = uniques[right], uniques[partition_idx] - return partition_idx + uniques[i], uniques[partitioned_idx] = uniques[partitioned_idx], uniques[i] + partitioned_idx += 1 + uniques[partitioned_idx], uniques[right] = uniques[right], uniques[partitioned_idx] + return partitioned_idx def quick_select(self, uniques, num_to_count, left, right, smallest_k): if left == right: @@ -26,14 +25,15 @@ def quick_select(self, uniques, num_to_count, left, right, smallest_k): return self.quick_select(uniques, num_to_count, partitioned_idx + 1, right, smallest_k) return - + + def topKFrequent(self, nums: List[int], k: int) -> List[int]: - num_to_count = {} + num_to_counts = {} for num in nums: - num_to_count.setdefault(num, 0) - num_to_count[num] += 1 - n = len(num_to_count) - uniques = list(num_to_count) - self.quick_select(uniques, num_to_count, 0, n - 1, n - k) + num_to_counts.setdefault(num, 0) + num_to_counts[num] += 1 + n = len(num_to_counts) + uniques = list(num_to_counts) + self.quick_select(uniques, num_to_counts, 0, n - 1, n - k) return uniques[n - k:] \ No newline at end of file From cf22a13560d490c229e827a8069a1d02f53c9b7b Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sun, 22 Feb 2026 18:00:00 +0900 Subject: [PATCH 10/11] =?UTF-8?q?step3=203=E5=9B=9E=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- step3-1.py | 10 +++++----- step3-2.py | 37 +++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/step3-1.py b/step3-1.py index 88802c0..3c1c8fc 100644 --- a/step3-1.py +++ b/step3-1.py @@ -3,16 +3,16 @@ class Solution: def topKFrequent(self, nums: List[int], k: int) -> List[int]: - num_to_counts = {} + num_to_count = {} for num in nums: - num_to_counts.setdefault(num, 0) - num_to_counts[num] += 1 + num_to_count.setdefault(num, 0) + num_to_count[num] += 1 topk_heap = [] - for num, count in num_to_counts.items(): + for num, count in num_to_count.items(): if len(topk_heap) < k: heapq.heappush(topk_heap, (count, num)) continue if count <= topk_heap[0][0]: continue heapq.heappushpop(topk_heap, (count, num)) - return [num for _, num in topk_heap] \ No newline at end of file + return [num for count, num in topk_heap] \ No newline at end of file diff --git a/step3-2.py b/step3-2.py index 804eabc..10c1bfe 100644 --- a/step3-2.py +++ b/step3-2.py @@ -2,38 +2,39 @@ import random class Solution: - def partition(self, uniques, num_to_count, left, right, pivot_idx): - pivot = num_to_count[uniques[pivot_idx]] - uniques[pivot_idx], uniques[right] = uniques[right], uniques[pivot_idx] + def partition(self, unique_nums, num_to_count, left, right, pivot_idx): + pivot = num_to_count[unique_nums[pivot_idx]] + unique_nums[pivot_idx], unique_nums[right] = unique_nums[right], unique_nums[pivot_idx] partitioned_idx = left for i in range(left, right): - if num_to_count[uniques[i]] <= pivot: - uniques[i], uniques[partitioned_idx] = uniques[partitioned_idx], uniques[i] + if num_to_count[unique_nums[i]] <= pivot: + unique_nums[i], unique_nums[partitioned_idx] = unique_nums[partitioned_idx], unique_nums[i] partitioned_idx += 1 - uniques[partitioned_idx], uniques[right] = uniques[right], uniques[partitioned_idx] + unique_nums[partitioned_idx], unique_nums[right] = unique_nums[right], unique_nums[partitioned_idx] return partitioned_idx + - def quick_select(self, uniques, num_to_count, left, right, smallest_k): + def quick_select(self, unique_nums, num_to_count, left, right, smallest_k): if left == right: return pivot_idx = random.randint(left, right) - partitioned_idx = self.partition(uniques, num_to_count, left, right, pivot_idx) + partitioned_idx = self.partition(unique_nums, num_to_count, left, right, pivot_idx) if partitioned_idx == smallest_k: return if partitioned_idx > smallest_k: - self.quick_select(uniques, num_to_count, left, partitioned_idx - 1, smallest_k) + self.quick_select(unique_nums, num_to_count, left, partitioned_idx - 1, smallest_k) return - self.quick_select(uniques, num_to_count, partitioned_idx + 1, right, smallest_k) + self.quick_select(unique_nums, num_to_count, partitioned_idx + 1, right, smallest_k) return - def topKFrequent(self, nums: List[int], k: int) -> List[int]: - num_to_counts = {} + num_to_count = {} for num in nums: - num_to_counts.setdefault(num, 0) - num_to_counts[num] += 1 - n = len(num_to_counts) - uniques = list(num_to_counts) - self.quick_select(uniques, num_to_counts, 0, n - 1, n - k) - return uniques[n - k:] + num_to_count.setdefault(num, 0) + num_to_count[num] += 1 + unique_nums = list(num_to_count) + n = len(unique_nums) + self.quick_select(unique_nums, num_to_count, 0, n - 1, n - k) + return unique_nums[n - k:] + \ No newline at end of file From 92be3c42866947cbb882f3cae075cbef39331a9e Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Sun, 22 Feb 2026 18:02:52 +0900 Subject: [PATCH 11/11] md --- memo.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/memo.md b/memo.md index 2ed6f45..41e8a36 100644 --- a/memo.md +++ b/memo.md @@ -300,4 +300,76 @@ class Solution: n = len(unique_nums) self.quick_select(unique_nums, num_to_counts, 0, n - 1, n - k) return unique_nums[n - k:] +``` + +# Step3 + +## Dict and Heap + +```python +# Heapを使用 +import heapq + +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + num_to_count = {} + for num in nums: + num_to_count.setdefault(num, 0) + num_to_count[num] += 1 + topk_heap = [] + for num, count in num_to_count.items(): + if len(topk_heap) < k: + heapq.heappush(topk_heap, (count, num)) + continue + if count <= topk_heap[0][0]: + continue + heapq.heappushpop(topk_heap, (count, num)) + return [num for count, num in topk_heap] + +``` + +## Quick Select + +```python +# QuickSelectを使用 +import random + +class Solution: + def partition(self, unique_nums, num_to_count, left, right, pivot_idx): + pivot = num_to_count[unique_nums[pivot_idx]] + unique_nums[pivot_idx], unique_nums[right] = unique_nums[right], unique_nums[pivot_idx] + partitioned_idx = left + for i in range(left, right): + if num_to_count[unique_nums[i]] <= pivot: + unique_nums[i], unique_nums[partitioned_idx] = unique_nums[partitioned_idx], unique_nums[i] + partitioned_idx += 1 + unique_nums[partitioned_idx], unique_nums[right] = unique_nums[right], unique_nums[partitioned_idx] + return partitioned_idx + + + def quick_select(self, unique_nums, num_to_count, left, right, smallest_k): + if left == right: + return + pivot_idx = random.randint(left, right) + partitioned_idx = self.partition(unique_nums, num_to_count, left, right, pivot_idx) + if partitioned_idx == smallest_k: + return + if partitioned_idx > smallest_k: + self.quick_select(unique_nums, num_to_count, left, partitioned_idx - 1, smallest_k) + return + self.quick_select(unique_nums, num_to_count, partitioned_idx + 1, right, smallest_k) + return + + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + num_to_count = {} + for num in nums: + num_to_count.setdefault(num, 0) + num_to_count[num] += 1 + unique_nums = list(num_to_count) + n = len(unique_nums) + self.quick_select(unique_nums, num_to_count, 0, n - 1, n - k) + return unique_nums[n - k:] + + + ``` \ No newline at end of file