-
Notifications
You must be signed in to change notification settings - Fork 0
703. Kth Largest Element in a Stream #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,57 @@ | ||
| ## step1 | ||
| - 二分探索木みたいなのに追加していって調べるのがいいのか? | ||
| - 普通のリストだと追加が遅いと思った | ||
| - 計算量がだいぶうろ覚え | ||
| - とりあえず最良ではないけど毎回並び替えて解くようにする | ||
| - sortが出てくるたびsortとsortedどっちがどっちだったかと降順・昇順どっちがデフォルトだったか毎回調べてる | ||
| - sort済みなのでbisect.insort()で追加していくO(logN)だったと思う | ||
| - 調べたら場所を調べるのはO(logN)だが追加はO(N)でやっぱり遅い | ||
| - 同じ数値だった場合左から追加か右から追加かは今回気にしない | ||
| - insortはinsort_rightと一緒みたい | ||
| - importのやり方も好まれるやり方がありそう | ||
| - https://note.nkmk.me/python-list-clear-pop-remove-del/ | ||
| - k番目を取り出す時にpopを使う時に末尾に近いほど計算量が少なくて済むので今回は小さい順に並んでた方が最悪計算量がマシになりそう | ||
| - 実際にはどのくらいの倍率かどうかを調べる必要がありそう | ||
| - VS Codeの拡張のtestが動かなくなった | ||
| - 抜き出さなくてよかった | ||
| - それなら毎回上位kだけ保持すればいい | ||
| - pop(0)はやっぱり遅い | ||
| - pop()にするためには値を全て負にして入れないといけなくなり手間がかかる。。。 | ||
| ## step2 | ||
| - heapqを使うと良いみたい | ||
| - 追加時にO(logN) | ||
| - K番目に大きい/小さい値heapq.nlargest(k, iterable) / nsmallest(k, iterable): (O(NlogK)) (Kが大きい場合は効率的) | ||
| - 一旦step2-1としてheapqの使い方を調べて解いてみる | ||
| - 多分やりたいことは合ってるはずだが、TLEした | ||
| - 計算量は最初heapq.heapifyでO(N)、addでO(NlogK) | ||
| - step1では最初のsortでO(NlogN),addでbisect.insortでO(N)でKが大きい時にstep1の方が早かった | ||
| - 今回の問題ではNと比較してKが小さいとかないので上位のみでカットする方法は最悪計算量は変わらなさそう | ||
| - 遅くなりことはおそらくないのでやれるならやった方が良い? | ||
| - 最小値の取得はheap[0] (最小値の取得): O(1)で行えるのでそこで早くなる(step2-2) | ||
| - これで劇的に早くなった26ms(メモリは25.71MB) | ||
|
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. この 26ms は LeetCode 上での実行時間でしょうか? |
||
| - step1も27msであまり変わらない(メモリは23.65MB) | ||
| - どっちが良いか考えたい | ||
| - 一旦他の人のコードを見る | ||
| - https://github.com/Hiroto-Iizuka/coding_practice/pull/8/files | ||
| - pushしてpopするための効率的な関数heapq.heappushpopがある | ||
| - https://github.com/mamo3gr/arai60/pull/8/files | ||
| - 全部入れてカットする方法はO(NlogN)で毎回カットする方ほうはO(NlogK)になる | ||
| - https://github.com/Yuto729/LeetCode_arai60/pull/14/files | ||
| - 計算時間が書いてあるのが良いと思った | ||
| - addにカットまで集約する | ||
| - 自前での実装はやっておくと、より親と子の位置関係などのイメージがつきそう | ||
| - https://github.com/kazizi55/coding-challenges/pull/8 | ||
| - `時間計算量から、どのくらいの処理時間がかかるか推定することをおすすめします。推定の方法は過去のコードレビューコメントにあると思いますので、探すことをおすすめします。` | ||
| - 処理時間の計算までしてなかったので、やるようにする | ||
| - `self.k -> self.capacity、self.top_k_nums -> self.largest_nums` | ||
| - 問題文のkを使うので問題はないと思うが、kという表記がない場合はcapacityはすごく良いと思った | ||
|
|
||
| ## step3 | ||
| - 詰まることなくかけた | ||
| - heapの計算量と操作をイメージしながら解いた | ||
|
|
||
|
|
||
|
|
||
|
|
||
| ## TODO | ||
| [] 自作heapの実装 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # | ||
| # @lc app=leetcode id=703 lang=python3 | ||
| # | ||
| # [703] Kth Largest Element in a Stream | ||
| # | ||
|
|
||
|
|
||
| # @lc code=start | ||
| from bisect import insort | ||
|
|
||
|
|
||
| class KthLargest: | ||
| def __init__(self, k: int, nums: List[int]): | ||
| self.k = k | ||
| self.sorted_nums = sorted(nums)[-k:] | ||
|
|
||
| def add(self, val: int) -> int: | ||
| insort(self.sorted_nums, val) | ||
| if len(self.sorted_nums) > self.k: | ||
| self.sorted_nums.pop(0) | ||
| return self.sorted_nums[0] | ||
|
|
||
|
|
||
| # Your KthLargest object will be instantiated and called as such: | ||
| # obj = KthLargest(k, nums) | ||
| # param_1 = obj.add(val)# @lc code=end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # | ||
| # @lc app=leetcode id=703 lang=python3 | ||
| # | ||
| # [703] Kth Largest Element in a Stream | ||
| # | ||
|
|
||
|
|
||
| # @lc code=start | ||
| from bisect import insort | ||
|
|
||
|
|
||
| class KthLargest: | ||
| def __init__(self, k: int, nums: List[int]): | ||
| self.k = k | ||
| self.sorted_nums = sorted(nums) | ||
|
|
||
| def add(self, val: int) -> int: | ||
| insort(self.sorted_nums, val) | ||
| return self.sorted_nums[-self.k] | ||
|
|
||
|
|
||
| # Your KthLargest object will be instantiated and called as such: | ||
| # obj = KthLargest(k, nums) | ||
| # param_1 = obj.add(val)# @lc code=end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # | ||
| # @lc app=leetcode id=703 lang=python3 | ||
| # | ||
| # [703] Kth Largest Element in a Stream | ||
| # | ||
|
|
||
| # @lc code=start | ||
| import heapq | ||
|
|
||
|
|
||
| class KthLargest: | ||
| def __init__(self, k: int, nums: List[int]): | ||
| self.k = k | ||
| self.heapifyed_values = nums | ||
| heapq.heapify(self.heapifyed_values) | ||
|
Comment on lines
+14
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.
|
||
|
|
||
| def add(self, val: int) -> int: | ||
| heapq.heappush(self.heapifyed_values, val) | ||
|
|
||
| return heapq.nlargest(self.k, self.heapifyed_values)[-1] | ||
|
|
||
|
|
||
| # Your KthLargest object will be instantiated and called as such: | ||
| # obj = KthLargest(k, nums) | ||
| # param_1 = obj.add(val) | ||
| # @lc code=end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # | ||
| # @lc app=leetcode id=703 lang=python3 | ||
| # | ||
| # [703] Kth Largest Element in a Stream | ||
| # | ||
|
|
||
| # @lc code=start | ||
| import heapq | ||
|
|
||
|
|
||
| class KthLargest: | ||
| def __init__(self, k: int, nums: List[int]): | ||
| self.k = k | ||
| self.heapifyed_values = nums | ||
| heapq.heapify(self.heapifyed_values) | ||
| self._trim_to_top_k() | ||
|
|
||
| def add(self, val: int) -> int: | ||
| heapq.heappush(self.heapifyed_values, val) | ||
| self._trim_to_top_k() | ||
|
|
||
| return self.heapifyed_values[0] | ||
|
|
||
| def _trim_to_top_k(self): | ||
| while len(self.heapifyed_values) > self.k: | ||
| heapq.heappop(self.heapifyed_values) | ||
|
Comment on lines
+24
to
+26
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. 関数に切り出すのいいですね。私の感覚、これくらいが切り出すか切り出さないかの境界くらいです。 |
||
|
|
||
|
|
||
| # Your KthLargest object will be instantiated and called as such: | ||
| # obj = KthLargest(k, nums) | ||
| # param_1 = obj.add(val) | ||
| # @lc code=end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # | ||
| # @lc app=leetcode id=703 lang=python3 | ||
| # | ||
| # [703] Kth Largest Element in a Stream | ||
| # | ||
|
|
||
| # @lc code=start | ||
| import heapq | ||
|
|
||
|
|
||
| class KthLargest: | ||
| def __init__(self, k: int, nums: List[int]): | ||
| self.k = k | ||
| self.top_k_values = [] | ||
| for n in nums: | ||
| self.add(n) | ||
|
|
||
| def add(self, val: int) -> int: | ||
| heapq.heappush(self.top_k_values, val) | ||
| self._trim_to_top_k() | ||
|
|
||
| return self.top_k_values[0] | ||
|
|
||
| def _trim_to_top_k(self): | ||
| while len(self.top_k_values) > self.k: | ||
| heapq.heappop(self.top_k_values) | ||
|
|
||
|
|
||
| # Your KthLargest object will be instantiated and called as such: | ||
| # obj = KthLargest(k, nums) | ||
| # param_1 = obj.add(val) | ||
| # @lc code=end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # | ||
| # @lc app=leetcode id=703 lang=python3 | ||
| # | ||
| # [703] Kth Largest Element in a Stream | ||
| # | ||
|
|
||
| # @lc code=start | ||
| import heapq | ||
|
|
||
|
|
||
| class KthLargest: | ||
| def __init__(self, k: int, nums: List[int]): | ||
| self.k = k | ||
| self.top_k_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. 今回のケースでは問題になりませんが、必ずしもソートされているわけではないので変数名がミスリーディングさせる可能性がありそうです(私もこう書いていたのですが…)。 |
||
|
|
||
| for n in nums: | ||
| self.add(n) | ||
|
|
||
| def add(self, val: int) -> int: | ||
| heapq.heappush(self.top_k_values, val) | ||
| self._trim_to_top_k() | ||
|
|
||
| return self.top_k_values[0] | ||
|
|
||
| def _trim_to_top_k(self): | ||
| while len(self.top_k_values) > self.k: | ||
| heapq.heappop(self.top_k_values) | ||
|
|
||
|
|
||
| # Your KthLargest object will be instantiated and called as such: | ||
| # obj = KthLargest(k, nums) | ||
| # param_1 = obj.add(val) | ||
| # @lc code=end | ||
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.
もし計算量を覚えようとしたということであれば、仕組みから導出できるようにもしたほうが良いかもしれません。そのほうが覚える量が少なくて済み、脳にやさしいかもしれません。
二分探索木は、子要素を辿っていくたびに、探索対象となるノードの数が半分になります。 2(探索回数) ≒ (ノード数) ですので、(探索回数) = log2(ノード数) で O(log N) になります。また、この考え方以外にも同様の考え方があると思います。ご自身にとって考えやすい考え方で理解し、忘れたときにも導出できるようにすることをおすすめします。
類題: 三角形の内角の和は何度ですか?また、その証明を導出できますか?