From 3921903d1c8e0eaaa809dc69eb61d6a987420cdd Mon Sep 17 00:00:00 2001 From: kazukiii Date: Wed, 12 Jun 2024 15:16:08 -0700 Subject: [PATCH 1/3] =?UTF-8?q?step1,=20step2,=20step3=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kth-largest-element-in-a-stream/README.md | 39 +++++++++++++++++++ .../kth-largest-element-in-a-stream/step1.py | 20 ++++++++++ .../kth-largest-element-in-a-stream/step2.py | 22 +++++++++++ .../kth-largest-element-in-a-stream/step3.py | 22 +++++++++++ 4 files changed, 103 insertions(+) create mode 100644 arai60/kth-largest-element-in-a-stream/README.md create mode 100644 arai60/kth-largest-element-in-a-stream/step1.py create mode 100644 arai60/kth-largest-element-in-a-stream/step2.py create mode 100644 arai60/kth-largest-element-in-a-stream/step3.py diff --git a/arai60/kth-largest-element-in-a-stream/README.md b/arai60/kth-largest-element-in-a-stream/README.md new file mode 100644 index 0000000..17a4f52 --- /dev/null +++ b/arai60/kth-largest-element-in-a-stream/README.md @@ -0,0 +1,39 @@ +## 考察 +- 過去に解いたことあり +- 常にk番目に大きい要素を高速に取り出せる状態にしておきたい +- 各クエリでkが固定なところもポイントだと思う +- 方針は4つ思い浮かぶ + - ソート済みnumsを保つ + - 二分探索で挿入ポイントを探して、insertする(insertにO(n)かかる) + - 初期化: O(n log n), 各クエリ: O(n) + - 最小ヒープを使う + - ヒープのサイズをkに保つ + - 初期化: O(n log k), 各クエリ: O(log k) + - fenwick tree上の二分探索(もしくはsegment tree上の二分探索) + - -10^4 <= nums[i] <= 10^4 かつ -10^4 <= val <= 10^4 なので、種類としては高々2 * 10^4 + 1 + - 各数字が何回現れたかを管理できる + - kが各クエリで変わるときは、この方法になると思う(今回はk固定なのでスキップ) + - 初期化: O(n log n), 各クエリ: O(log n) + - 平衡二分探索木 + - たぶん最終手段だと思う + - Pythonにはライブラリはない、一から実装することになりそう(かなり大変そうなイメージ、実装したことはない) +- 最も効率の良い最小ヒープでやる +- あとは実装 + +## Step1 +- 最小ヒープで実装 +- time: O(n log k), space: O(k) + +## Step2 +- 他の人のPRを検索してみた +- Pythonにはheappushpopというのがあるらしい + - Ref. https://github.com/kagetora0924/leetcode-grind/pull/9#discussion_r1633996701 + - heappush()とheappop()を個別に呼び出すよりも効率が良い + - `Push item on the heap, then pop and return the smallest item from the heap. The combined action runs more efficiently than heappush() followed by a separate call to heappop().` + - Ref. https://docs.python.org/3/library/heapq.html#heapq.heappushpop +- heappushpopを使ってみた + +## Step3 +- 1回目: 1m46s +- 2回目: 1m51s +- 3回目: 1m42s diff --git a/arai60/kth-largest-element-in-a-stream/step1.py b/arai60/kth-largest-element-in-a-stream/step1.py new file mode 100644 index 0000000..a573e99 --- /dev/null +++ b/arai60/kth-largest-element-in-a-stream/step1.py @@ -0,0 +1,20 @@ +class KthLargest: + def __init__(self, k: int, nums: List[int]): + self.k = k + self.num_min_heap = [] + for num in nums: + heapq.heappush(self.num_min_heap, num) + if len(self.num_min_heap) > self.k: + heapq.heappop(self.num_min_heap) + + def add(self, val: int) -> int: + heapq.heappush(self.num_min_heap, val) + if len(self.num_min_heap) > self.k: + heapq.heappop(self.num_min_heap) + return self.num_min_heap[0] + + + +# Your KthLargest object will be instantiated and called as such: +# obj = KthLargest(k, nums) +# param_1 = obj.add(val) diff --git a/arai60/kth-largest-element-in-a-stream/step2.py b/arai60/kth-largest-element-in-a-stream/step2.py new file mode 100644 index 0000000..052b2ac --- /dev/null +++ b/arai60/kth-largest-element-in-a-stream/step2.py @@ -0,0 +1,22 @@ +class KthLargest: + def __init__(self, k: int, nums: List[int]): + self.k = k + self.num_min_heap = [] + for num in nums: + if len(self.num_min_heap) < self.k: + heapq.heappush(self.num_min_heap, num) + else: + heapq.heappushpop(self.num_min_heap, num) + + def add(self, val: int) -> int: + if len(self.num_min_heap) < self.k: + heapq.heappush(self.num_min_heap, val) + else: + heapq.heappushpop(self.num_min_heap, val) + return self.num_min_heap[0] + + + +# Your KthLargest object will be instantiated and called as such: +# obj = KthLargest(k, nums) +# param_1 = obj.add(val) diff --git a/arai60/kth-largest-element-in-a-stream/step3.py b/arai60/kth-largest-element-in-a-stream/step3.py new file mode 100644 index 0000000..f9246b2 --- /dev/null +++ b/arai60/kth-largest-element-in-a-stream/step3.py @@ -0,0 +1,22 @@ +class KthLargest: + def __init__(self, k: int, nums: List[int]): + self.k = k + self.num_min_heap = [] + for num in nums: + if len(self.num_min_heap) < k: + heapq.heappush(self.num_min_heap, num) + else: + heapq.heappushpop(self.num_min_heap, num) + + def add(self, val: int) -> int: + if len(self.num_min_heap) < self.k: + heapq.heappush(self.num_min_heap, val) + else: + heapq.heappushpop(self.num_min_heap, val) + return self.num_min_heap[0] + + + +# Your KthLargest object will be instantiated and called as such: +# obj = KthLargest(k, nums) +# param_1 = obj.add(val) From f38202417f39ab39b10369a1d9098c99726370bb Mon Sep 17 00:00:00 2001 From: kazukiii Date: Thu, 13 Jun 2024 15:00:03 -0700 Subject: [PATCH 2/3] =?UTF-8?q?tree=E3=81=A8sortedlist=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../step1_with_sortedlist.py | 16 ++++++++++ .../step1_with_tree.cpp | 31 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 arai60/kth-largest-element-in-a-stream/step1_with_sortedlist.py create mode 100644 arai60/kth-largest-element-in-a-stream/step1_with_tree.cpp diff --git a/arai60/kth-largest-element-in-a-stream/step1_with_sortedlist.py b/arai60/kth-largest-element-in-a-stream/step1_with_sortedlist.py new file mode 100644 index 0000000..e0a333a --- /dev/null +++ b/arai60/kth-largest-element-in-a-stream/step1_with_sortedlist.py @@ -0,0 +1,16 @@ +from sortedcontainers import SortedList + +class KthLargest: + def __init__(self, k: int, nums: List[int]): + self.k = k + self.tree = SortedList(nums) + + def add(self, val: int) -> int: + self.tree.add(val) + return self.tree[len(self.tree) - self.k] + + + +# Your KthLargest object will be instantiated and called as such: +# obj = KthLargest(k, nums) +# param_1 = obj.add(val) diff --git a/arai60/kth-largest-element-in-a-stream/step1_with_tree.cpp b/arai60/kth-largest-element-in-a-stream/step1_with_tree.cpp new file mode 100644 index 0000000..340c6d4 --- /dev/null +++ b/arai60/kth-largest-element-in-a-stream/step1_with_tree.cpp @@ -0,0 +1,31 @@ +#include +#include + +using namespace __gnu_pbds; +template +using ordered_multiset = tree, rb_tree_tag, tree_order_statistics_node_update>; + +class KthLargest { +private: + int k; + ordered_multiset binary_search_tree; + +public: + KthLargest(int k, vector& nums) : k(k) { + for (int num : nums) { + binary_search_tree.insert(num); + } + } + + int add(int val) { + binary_search_tree.insert(val); + auto it = binary_search_tree.find_by_order(binary_search_tree.size() - k); + return *it; + } +}; + +/** + * Your KthLargest object will be instantiated and called as such: + * KthLargest* obj = new KthLargest(k, nums); + * int param_1 = obj->add(val); + */ From d8703446897b66d7927abdab1699cb89d94cce26 Mon Sep 17 00:00:00 2001 From: kazukiii Date: Thu, 13 Jun 2024 15:15:42 -0700 Subject: [PATCH 3/3] =?UTF-8?q?step4=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kth-largest-element-in-a-stream/README.md | 4 ++++ .../kth-largest-element-in-a-stream/step4.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 arai60/kth-largest-element-in-a-stream/step4.py diff --git a/arai60/kth-largest-element-in-a-stream/README.md b/arai60/kth-largest-element-in-a-stream/README.md index 17a4f52..e9baa8f 100644 --- a/arai60/kth-largest-element-in-a-stream/README.md +++ b/arai60/kth-largest-element-in-a-stream/README.md @@ -37,3 +37,7 @@ - 1回目: 1m46s - 2回目: 1m51s - 3回目: 1m42s + +## Step4 +- レビューを元に修正 +- `__init__` 内で、`add` を流用できたためシンプルになった diff --git a/arai60/kth-largest-element-in-a-stream/step4.py b/arai60/kth-largest-element-in-a-stream/step4.py new file mode 100644 index 0000000..2b54e94 --- /dev/null +++ b/arai60/kth-largest-element-in-a-stream/step4.py @@ -0,0 +1,19 @@ +class KthLargest: + def __init__(self, k: int, nums: List[int]): + self.k = k + self.num_min_heap = [] + for num in nums: + self.add(num) + + def add(self, val: int) -> int: + if len(self.num_min_heap) < self.k: + heapq.heappush(self.num_min_heap, val) + else: + heapq.heappushpop(self.num_min_heap, val) + return self.num_min_heap[0] + + + +# Your KthLargest object will be instantiated and called as such: +# obj = KthLargest(k, nums) +# param_1 = obj.add(val)