Skip to content

703. Kth Largest Element in a Stream.md #8

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
155 changes: 155 additions & 0 deletions 703. Kth Largest Element in a Stream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
URL: https://leetcode.com/problems/kth-largest-element-in-a-stream/description/

# Step 1

- 実装時間: 15分
- 時間計算量:
- init: O(nlog n)
- add: O(log n)
- 空間計算量:
- init: O(k)

Choose a reason for hiding this comment

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

sorted(nums, reverse=True)が新しく配列を作るのでStep1はO(n)になるかと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

空間計算量の方ですね、たしかにです!!ありがとうございます!!

- サイズkのヒープself.scores_heapのみを使用する。
- add: O(1)

```python
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self.k = k
self.scores_heap = []
Copy link

Choose a reason for hiding this comment

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

heapq.heapify() を使うともう少しシンプルになるかと思ったのですが、ならなそうですね…。

for num in sorted(nums, reverse=True)[:k]:
Copy link

@colorbox colorbox Nov 27, 2024

Choose a reason for hiding this comment

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

好みかもしれませんが、sortedした後にheappushしているのが気になりました。
for文の中でheapq内の要素数がk個を超える時にheappopするとよさそうです。

heapq.heappush(self.scores_heap, num)

def add(self, val: int) -> int:
if val <= self.scores_heap[0]:
return self.scores_heap[0]
heapq.heappop(self.scores_heap)
heapq.heappush(self.scores_heap, val)
return self.scores_heap[0]
```

与えられたscoreがk未満の状態でエラーになったので修正。

```python
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self.k = k
self.scores_heap = []
for num in sorted(nums, reverse=True)[:k]:
heapq.heappush(self.scores_heap, num)

def add(self, val: int) -> int:
if len(self.scores_heap) == self.k and val <= self.scores_heap[0]:
return self.scores_heap[0]
if len(self.scores_heap) == self.k:
heapq.heappop(self.scores_heap)
heapq.heappush(self.scores_heap, val)
return self.scores_heap[0]
```

# Step 2
- 参考にしたURL
- https://github.com/kazukiii/leetcode/pull/9
- https://github.com/Yoshiki-Iwasa/Arai60/pull/7
- https://github.com/seal-azarashi/leetcode/pull/8
- https://github.com/haniwachann/leetcode/pull/1
- https://github.com/tarinaihitori/leetcode/pull/8
- https://github.com/hroc135/leetcode/pull/8
- https://github.com/colorbox/leetcode/pull/23
- https://github.com/SuperHotDogCat/coding-interview/pull/38
- https://github.com/ryoooooory/LeetCode/pull/15
- https://github.com/cheeseNA/leetcode/pull/12
- https://github.com/goto-untrapped/Arai60/pull/23
- https://github.com/Ryotaro25/leetcode_first60/pull/9

- initでもaddを呼ぶとシンプルになる。

- priority queueとheapの違い
- https://github.com/cheeseNA/leetcode/pull/12/files#r1543919411

- quick sortの常識
- https://discord.com/channels/1084280443945353267/1200089668901937312/1203725416645271582
- https://discord.com/channels/1084280443945353267/1183683738635346001/1185972070165782688

- 変数名
- プライベート(とみなしたい)名前に`_`を足すルールは、プライベート関数だけだと間違えて理解してた。
- 関数、メソッド、データ メンバーのいずれであっても同様。
- https://docs.python.org/3/tutorial/classes.html#private-variables

- `scores_heap`の変数名について
- 上位k番目までしか保持しないので、この名前だと全量持つような感じを与える。
- 型の名前を不必要に変数名に含めない
- scores_heap
- `_kth_largest_nums`が良さそう

- JavaのPriorityQueue
- push/pop/topがそれぞれoffer/poll/peekだと知った。名前が新鮮に感じた。
- addは別にある。
- https://docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html

- https://docs.python.org/3/howto/sorting.html
- 読んだ。
- stableにするために、Decorate-Sort-Undecorateでindexを考慮する。
- The Timsort algorithm used in Python
- 3.11から変わってる。
- https://www.i-programmer.info/news/216-python/15954-python-now-uses-powersort.html

- heappushpop
- >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().
- https://docs.python.org/3/library/heapq.html#heapq.heappushpop

- > この問題で priority_queue にいきなりいくのは、私は実は違和感があります。
- https://github.com/Ryotaro25/leetcode_first60/pull/9/files#r1619710596
- > 平衡木は、どれも実装が大変なので、人生で一回書け、とまでは言い難いです。しかし、どれか一つくらいはどういうものであるか説明できるようにしておくといいんじゃないでしょうか。

- > priority queueは一度実装しておいたほうがよい。
- https://discord.com/channels/1084280443945353267/1192736784354918470/1194613857046503444

- クイックセレクトについて
- quickソートで並び替えつつ、特定の位置の要素を確定させに行く。
- > Quick Select も常識範囲
- https://discord.com/channels/1084280443945353267/1183683738635346001/1186004009417449602

- kが負の場合について
- 今回は問題上、出てこない。
- プラスアルファで検討しても良かった。弾くならinitで弾くべき
Copy link

Choose a reason for hiding this comment

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

負の場合、[:k] のところで、予期しないスライスができるか落ちるかで、中途半端にそれらしい値が出てくることになるでしょう。
あまり意味のないものがそれっぽく動き続けるのは、わりとデバッグのときに困ります。

このあたりは、どういう環境に置くか次第なところはあります。
たとえば、ユーザーフェイシングならば、なんらかの結果が見えたほうがいい場合もあるでしょう。夜中に毎日走るパイプラインなどだったらエラーを出して止まってくれたほうが嬉しいでしょう。
常に使い道からこうしたいまでをつなげてください。

- https://github.com/tarinaihitori/leetcode/pull/8/files#r1815830919

```python
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self._k = k
self._kth_largest_nums = []
for num in nums:
self.add(num)

def add(self, val: int) -> int:
heapq.heappush(self._kth_largest_nums, val)
if len(self._kth_largest_nums) > self._k:
heapq.heappop(self._kth_largest_nums)
return self._kth_largest_nums[0]
```

# Step 3

- 時間計算量:
- init: O(nlog n)

Choose a reason for hiding this comment

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

k個以上はヒープに入れないようにしているので、O(nlogk)だと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

時間計算量はなので、k個以上はヒープに入れないようにしているとはいえ、for num in nums:でn個みちゃってるので、O(n log n)かなと思ったんですが、どうでしょう。。。

Copy link
Owner Author

Choose a reason for hiding this comment

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

あ、ちがう。間違いに気づきました。
1回あたりの挿入がO(log k)だからってことですよね!なるほどです

- add: O(log n)

Choose a reason for hiding this comment

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

こちらもO(logk)

- 空間計算量:
- init: O(k)

Choose a reason for hiding this comment

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

_kth_largest_numsはインスタンス自体の変数なので、その意味だとO(1)かもしれないです

Copy link
Owner Author

Choose a reason for hiding this comment

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

ごめんなさい・・指摘が理解できてないです 🙇

インスタンス自体の変数だと追加で使用する容量は固定ということ・・?

Choose a reason for hiding this comment

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

_kth_largest_numsはインスタンスの変数として定義されているので、init関数としては追加のメモリは必要としていないのではないでしょうかということでした。

Copy link
Owner Author

Choose a reason for hiding this comment

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

あぁ!ありがとうございます、おっしゃりたいことわかりました。

ちょっと悩ましいのが、僕のこの実装だと、__init__の中で、add関数をn回呼ぶので、
init関数単体ではO(1)とも言える。けど、呼び先のaddも入れるとinitの処理終了までに、結局(最大で)要素k個分の領域を使用する」
みたいな感じです。

なので、処理終了までの話を見たときには、

  • init: O(k)
  • add: O(1)

だし、純粋にその関数固有の処理を見たときには

  • init: O(1)
  • add: O(k)
    と書いた方がいいのか。と気付きました。

Choose a reason for hiding this comment

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

まだ自分の認識とあっていないです。
_kth_largest_numsはinitともaddとも違う場所に保存されているので、init,addからheappushなどを読んだとしてもinit, add関数としては追加のメモリを使用しないというつもりでした。

- add: O(1)

```python
class KthLargest:

def __init__(self, k: int, nums: List[int]):
self._k = k
self._kth_largest_nums = []
for num in nums:
self.add(num)

def add(self, val: int) -> int:
heappush(self._kth_largest_nums, val)
if len(self._kth_largest_nums) > self._k:
heappop(self._kth_largest_nums)
return self._kth_largest_nums[0]
```