Skip to content
Open
Show file tree
Hide file tree
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
43 changes: 43 additions & 0 deletions arai60/kth-largest-element-in-a-stream/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## 考察
- 過去に解いたことあり
- 常に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上の二分探索)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(コードへのコメントではないですが。) fenwick 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)
- 平衡二分探索木
- たぶん最終手段だと思う
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Python で使いたかったら、標準でないライブラリーがあります。C++ では標準で平衡木がありますね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

こちら調べてみました。

  • C++標準のmultisetを使うと、前から辿る必要があるので、kth elementをO(k)で取得可能
  • g++拡張の__gnu_pbdsにあるtreeを使うと、各ノードにsub treeのサイズを持たせられるオプションがあり、kth elemntをO(log n)で取得可能(LeetCodeでも使えました)
  • PythonでもLeetCodeで使えるものを探してみたところ、sortedcontainerのSortedListを使うとO(log n)でkth elementを取得できた

実装を追加してます。-> f382024

- 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

## Step4
- レビューを元に修正
- `__init__` 内で、`add` を流用できたためシンプルになった
20 changes: 20 additions & 0 deletions arai60/kth-largest-element-in-a-stream/step1.py
Original file line number Diff line number Diff line change
@@ -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)
16 changes: 16 additions & 0 deletions arai60/kth-largest-element-in-a-stream/step1_with_sortedlist.py
Original file line number Diff line number Diff line change
@@ -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)
31 changes: 31 additions & 0 deletions arai60/kth-largest-element-in-a-stream/step1_with_tree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace __gnu_pbds;
template <typename T>
using ordered_multiset = tree<T, null_type, less_equal<T>, rb_tree_tag, tree_order_statistics_node_update>;

class KthLargest {
private:
int k;
ordered_multiset<int> binary_search_tree;

public:
KthLargest(int k, vector<int>& 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);
*/
22 changes: 22 additions & 0 deletions arai60/kth-largest-element-in-a-stream/step2.py
Original file line number Diff line number Diff line change
@@ -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)
22 changes: 22 additions & 0 deletions arai60/kth-largest-element-in-a-stream/step3.py
Original file line number Diff line number Diff line change
@@ -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)
Comment on lines +5 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちら、addを呼ぶといいと思います。

for num in nums:
    self.add(num)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

気付きませんでした。シンプルになりますね。
ありがとうございます。


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)
19 changes: 19 additions & 0 deletions arai60/kth-largest-element-in-a-stream/step4.py
Original file line number Diff line number Diff line change
@@ -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)