-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add 703. Kth Largest Element in a Stream.md #8
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,199 @@ | ||
# step 1 | ||
|
||
思いついた解法(n = これまで入力された要素数とする) | ||
- sortされたリスト (要素数k分だけ昇順で記録し、list[0]でアクセス) | ||
- init | ||
- time complexity: O(n log(n)) | ||
- space complexity: O(n) (Aux: O(1)) | ||
- add | ||
- time complexity: O(k) | ||
- space complexity: O(k) (Aux: O(1)) | ||
- sortされていないリスト (全ての要素を記録し、quickselect) | ||
- init | ||
- time complexity: O(1) | ||
- space complexity: O(n) (Aux: O(1)) | ||
- add | ||
- time complexity: O(n^2) (average: O(n)) | ||
- space complexity: O(n) (Aux: O(1)) | ||
- heap (要素数k分だけ記録し、heap[0]でアクセス) | ||
- init | ||
- time complexity: O(n log(k)) (heappushをn回繰り返す場合) | ||
- space complexity: O(k) | ||
- add | ||
- time complexity: O(log(k)) | ||
- space complexity: O(k) (Aux: O(1)) | ||
|
||
heapを使った解法 | ||
```python | ||
import heapq | ||
|
||
|
||
class KthLargest: | ||
|
||
def __init__(self, k: int, nums: List[int]): | ||
self.heap: List[int] = [] | ||
self.k = k | ||
for num in nums: | ||
if len(self.heap) < self.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. len(self.heap) == self.k のときに push されないように見えます。 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. コメントありがとうございます。 とりあえずpushをして、kを超える要素数があればpopする方がみよいですね。 |
||
heapq.heappush(self.heap, num) | ||
continue | ||
heapq.heappushpop(self.heap, num) | ||
|
||
def add(self, val: int) -> int: | ||
heapq.heappush(self.heap, val) | ||
if len(self.heap) == self.k + 1: | ||
heapq.heappop(self.heap) | ||
return self.heap[0] | ||
``` | ||
|
||
$1 <= k <= nums.length$という問題設定に依存している回答になった。`add()`において、KthLargestが存在しない場合、これまでの最小値を返す様にしている。 | ||
|
||
`__init__()`にて、heapを作るのに、以下の手法を思いついた。 | ||
- heappushをn回繰り返す(O(n log(k))) | ||
- sortして、長さk分だけ取り出す(O(n log(n))) | ||
- heapfyして、n-kだけheappopする(O(n + (n-k)log(n))) | ||
|
||
|
||
quickselectを使った解法 | ||
```python | ||
import random | ||
|
||
|
||
class KthLargest: | ||
|
||
def __init__(self, k: int, nums: List[int]): | ||
self.array = nums | ||
self.k = k | ||
|
||
def add(self, val: int) -> int: | ||
def quickselect( | ||
array: List[int], | ||
left: int, | ||
right: int, | ||
index: int | ||
) -> int: | ||
pivot_index = random.randint(left, right) | ||
array[pivot_index], array[right] = array[right], array[pivot_index] | ||
|
||
pivot, less_index = array[right], left | ||
for i in range(left, right): | ||
if array[i] <= pivot: | ||
array[i], array[less_index] = array[less_index], array[i] | ||
less_index += 1 | ||
array[less_index], array[right] = array[right], array[less_index] | ||
if less_index == index: | ||
return array[less_index] | ||
elif less_index < index: | ||
return quickselect(array, less_index + 1, right, index) | ||
else: | ||
return quickselect(array, left, less_index - 1, index) | ||
|
||
self.array.append(val) | ||
length = len(self.array) | ||
return quickselect(self.array, 0, length - 1, length - self.k) | ||
``` | ||
partitionを左右から狭めていくタイプのやり方を実装しようとして、10分ほど考えて、うまく動かなかったので、https://www.youtube.com/watch?v=XEmy13g1Qxc&ab_channel=NeetCode を見た。 | ||
定数倍遅い解法だがquickselectの参考となった。 | ||
|
||
addを呼ぶ回数の多いテストケースにてTLEした。 | ||
|
||
# step 2 | ||
- [quicksortのpartiton](https://www.geeksforgeeks.org/hoares-vs-lomuto-partition-scheme-quicksort/) | ||
- pythonのlistのlength: | ||
- https://github.com/python/cpython/blob/cef0a90d8f3a94aa534593f39b4abf98165675b9/Include/cpython/listobject.h#L30-L35 | ||
- https://github.com/python/cpython/blob/main/Objects/listobject.c#L299-L309 | ||
- 構造体に長さを記録しているので、よばれる度にループを回して要素数を数えているわけではない | ||
- https://github.com/rinost081/LeetCode/pull/9/files | ||
- 関数を呼び出す側としては、ある関数を呼び出したときに、渡した引数の中身が変更されることは、あまり想定しないように思います。関数内では原則引数の中身を変更しないことをお勧めいたします。また、特別な理由があって変更する場合は、関数コメント等に明示的にその旨を書くことをお勧めいたします。 | ||
- python3.11のsortアルゴリズム (Timsort -> Powersort) | ||
https://www.i-programmer.info/news/216-python/15954-python-now-uses-powersort.html | ||
- https://github.com/BumbuShoji/Leetcode/pull/9 | ||
- (heapの解法にて):heapの要素数がself.kよりも長くならないことはクラス全体を見ないと分からない実装になっている | ||
- https://github.com/konnysh/arai60/pull/8/files | ||
|
||
思ったこと | ||
- 変数名について、heapを用いる解法では`kth_largest`とかそういう名前が多かった。確かに、`heapq.heappush`とかやっているので`heap`だけでは不十分だと感じた。 | ||
- `__init__()`にて`add()`を呼ぶ解法も多かった。コードの重複がないので、その解法の方が、コンパクトに収まる様に感じた。 | ||
|
||
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. https://docs.python.org/3/library/heapq.html#heapq.heappushpop heapq の標準ドキュメントを見たことがなければざっと目を通しておいてください。たとえば、heappushpop などがあったりします。dict を使うたびに見る、みたいなのはやりすぎだとは思いますが、時々目を通すといいでしょう。 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. (おっと、heappushpop 使っていましたね。) |
||
partitionにてHoaresをやろうとして、off-by-one errorが取れなかった。 | ||
引数が何で、何をするということの言語化がちゃんとできていないことが原因だと思う。日常に即した具体例とか作れると良さそう(思いつかない)。 | ||
|
||
```python | ||
import heapq | ||
|
||
|
||
class KthLargest: | ||
|
||
def __init__(self, k: int, nums: List[int]): | ||
if k <= 0 or k > len(nums) + 1: | ||
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.
|
||
raise ValueError(f'Invalid input value k = {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. https://docs.python.org/3/library/exceptions.html#ValueError |
||
self.kth_largest_scores: List[int] = [] | ||
self.k: int = k | ||
for num in nums: | ||
self.add(num) | ||
|
||
def add(self, val: int) -> int: | ||
heapq.heappush(self.kth_largest_scores, val) | ||
while len(self.kth_largest_scores) > self.k: | ||
heapq.heappop(self.kth_largest_scores) | ||
return self.kth_largest_scores[0] | ||
``` | ||
|
||
# step 3 | ||
```python | ||
import heapq | ||
|
||
|
||
class KthLargest: | ||
|
||
def __init__(self, k: int, nums: List[int]): | ||
if k < 1 or k > len(nums) + 1: | ||
raise ValueError(f'Invalid argument: ' | ||
f'KthLargest class constructor value k = {k}') | ||
self.k: int = k | ||
self.kth_largest_scores: List[int] = [] | ||
for num in nums: | ||
self.add(num) | ||
|
||
def add(self, val: int) -> int: | ||
heapq.heappush(self.kth_largest_scores, val) | ||
while len(self.kth_largest_scores) > self.k: | ||
heapq.heappop(self.kth_largest_scores) | ||
return self.kth_largest_scores[0] | ||
``` | ||
|
||
# step 4 | ||
コメントまとめ | ||
- クラスの次の行は開けないことが多い気がする | ||
- `if k < 1 or k > len(nums) + 1:`よりは、`if not 1 <= k <= len(nums) + 1:`のほうがわかりやすい。 | ||
- [builtin exceptions](https://docs.python.org/3/library/exceptions.html#built-in-exceptions) | ||
|
||
メモ | ||
- [timsort](https://hal.science/hal-01212839) | ||
- [powersort](https://drops.dagstuhl.de/entities/document/10.4230/LIPIcs.ESA.2018.63) | ||
- https://github.com/python/cpython/blob/48c70b8f7dfd00a018abbac50ea987f54fa4db51/Objects/listsort.txt | ||
|
||
|
||
```python | ||
import heapq | ||
|
||
|
||
class KthLargest: | ||
|
||
def __init__(self, k: int, nums: List[int]): | ||
if not 1 <= k <= len(nums) + 1: | ||
raise ValueError( | ||
"KthLargest constructor: k is a invalid size for nums: " | ||
f"k = {k}, nums = {nums}" | ||
) | ||
self.k: int = k | ||
self.kth_largest_heap: List[int] = [] | ||
for num in nums: | ||
self.add(num) | ||
|
||
def add(self, val: int) -> int: | ||
heapq.heappush(self.kth_largest_heap, val) | ||
while len(self.kth_largest_heap) > self.k: | ||
heapq.heappop(self.kth_largest_heap) | ||
return self.kth_largest_heap[0] | ||
``` |
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.
class の次の行は空けないことが多い気がしますが趣味の範囲でしょう。