-
Notifications
You must be signed in to change notification settings - Fork 0
Create KthLargestElementInAStream.md #14
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,124 @@ | ||
| # step1 何も見ずに解く | ||
| - PriorityQueueのカテゴリなので一旦それを使う前提で考える | ||
| - PriorityQueueを使用したことがないのでリファレンスを参照する | ||
| - メソッド名に馴染みが薄い | ||
| - `offer`: 要素を追加 | ||
| - `poll`: 最優先要素を取り出して削除 | ||
| - 内部的には配列を使用した完全二分木で実装されている | ||
|
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.
こちらですが、少し不正確に感じました。 正しくは「配列で表現されたheapというデータ構造を用いて実装されている」となると思います。
heapの知識はSWEの常識に含まれると思うので、もしご存知でなければご確認いただくことをお勧めします。
Owner
Author
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. 恥ずかしながら、各タームの定義が曖昧なまま書いていました。 完全二分木: 各レベルが左から順に埋まっている二分木 |
||
| - Priorityがn番目に高い要素を取り出そうと思ったらそこまで`poll()`しないと取り出せなさそう | ||
| - ということはこの場合は、取り出したい値が常に最優先になるように実装するのが良さそう | ||
| - 入力(nums)がk個未満の場合は何を返すのがいいのだろう | ||
| - 一番低い点数が返ってくるのが自然に感じる | ||
|
Comment on lines
+10
to
+11
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. こちら、一案として良いと思いました。 |
||
| ## 解答 | ||
| - PriorityQueueを利用した実装 | ||
| ```java | ||
| class KthLargest { | ||
| // k番目に大きい要素までだけを保持 | ||
| private Queue<Integer> kthLargestScores = new PriorityQueue<>(); | ||
|
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. PriorityQueue のほうがよいように思います。PriorityQueue としての性質を使っているので。
Owner
Author
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. Interfaceに定義されていないクラス固有の性質に依存している場合は具体的なクラスで型宣言するということですね。 |
||
| private int k; | ||
|
|
||
| public KthLargest(int k, int[] nums) { | ||
| this.k = k; | ||
| for (int num : nums) { | ||
| add(num); | ||
| } | ||
| } | ||
|
|
||
| public int add(int val) { | ||
| kthLargestScores.offer(val); | ||
| if (kthLargestScores.size() > k) { | ||
| kthLargestScores.poll(); | ||
| } | ||
| return kthLargestScores.peek(); | ||
| } | ||
| } | ||
| ``` | ||
| - ソートされたArrayListを用いる方法 | ||
| - ArrayListで全ての要素を保持して、k番目の要素を返す | ||
| - 毎回ソートすると、コストが大きいので常にソートされた状態にして二分探索で挿入位置を求めるようにしておく | ||
| - 最初書いた時comparatorを指定せず、List昇順のままにしていて意図した動作せず | ||
| - 後から気づいたが、わざわざ降順にしなくても配列の末尾からk番目の要素を取得すればよかった | ||
| - add()の返り値を取得する際に以下のようにする | ||
| - `return kthLargestScores.get(kthLargestScores.size() - k);` | ||
| ```java | ||
| class KthLargest { | ||
| private List<Integer> kthLargestScores = new ArrayList<>(); | ||
| private int k; | ||
| private Comparator<Integer> desc = Comparator.reverseOrder(); | ||
|
|
||
| public KthLargest(int k, int[] nums) { | ||
| this.k = k; | ||
| kthLargestScores.addAll( | ||
| Arrays.stream(nums).boxed().sorted(desc).toList() | ||
| ); | ||
| } | ||
|
|
||
| public int add(int val) { | ||
| int insertIndex = Collections.binarySearch(kthLargestScores, val, desc); | ||
| if (insertIndex < 0) { | ||
| insertIndex = -(insertIndex + 1); | ||
| } | ||
| kthLargestScores.add(insertIndex, val); | ||
| // indexは0basedのため-1する | ||
| return kthLargestScores.get(k - 1); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| # step2 他の方の解答を見る | ||
| - クラスのプロパティとして`topKScores`という命名が自然だと感じた | ||
| - コンストラクタに渡されるkが1以上であることをチェックしている実装もある | ||
| - 確かにチェックしてある方が親切かも | ||
| ## 解答 | ||
| - 変数名を少し修正、コンストラクで引数チェックを追加 | ||
| ```java | ||
| class KthLargest { | ||
| private Queue<Integer> topKScores = new PriorityQueue<>(); | ||
| private int k; | ||
|
|
||
| public KthLargest(int k, int[] nums) { | ||
| if (k <= 0) { | ||
| throw new IllegalArgumentException("k must be larger than 0. k : " + k); | ||
| } | ||
| this.k = k; | ||
| for (int num : nums) { | ||
| add(num); | ||
| } | ||
| } | ||
|
|
||
| public int add(int val) { | ||
| topKScores.offer(val); | ||
| if (topKScores.size() > k) { | ||
| topKScores.poll(); | ||
| } | ||
| return topKScores.peek(); | ||
| } | ||
|
Comment on lines
+89
to
+95
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. 現在は毎回offerした後にsize > kならpollをしていますが、先に.peek()と比較して不要な挿入を避けるとヒープ操作回数を減らせて有利になる気がします。 public int add(int val) {
if (minHeap.size() < k) {
minHeap.offer(val);
} else if (val > minHeap.peek()) {
minHeap.poll();
minHeap.offer(val);
}
return minHeap.peek();
}
Owner
Author
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. 複雑性とのトレードオフがどうかなとも思いますが、二分ヒープの内部構造を考えると不要なsiftUp()を撲滅できてとても効果的な提案だと思います! |
||
| } | ||
| ``` | ||
|
|
||
| # step3 3回ミスなく書く | ||
| ## 解答 | ||
| - PriorityQueueを使って実装 | ||
| - Step2と同様の感じ | ||
| - 今回の入力の制限に沿って、入力値チェックは省略 | ||
| ```java | ||
| class KthLargest { | ||
| private Queue<Integer> topKScores = new PriorityQueue<>(); | ||
| private int k; | ||
|
|
||
| public KthLargest(int k, int[] nums) { | ||
| this.k = k; | ||
| for (int num : nums) { | ||
| add(num); | ||
| } | ||
| } | ||
|
|
||
| public int add(int val) { | ||
| topKScores.offer(val); | ||
| if (topKScores.size() > k) { | ||
| topKScores.poll(); | ||
| } | ||
| return topKScores.peek(); | ||
| } | ||
| } | ||
| ``` | ||
|
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. 今回のケースでは、kは最初に与えられたら不変なので、 import java.util.PriorityQueue;
import java.util.Queue;
class KthLargest {
private final Queue<Integer> topKScores;
private final int k;
public KthLargest(int k, int[] nums) {
this.k = k;
this.topKScores = new PriorityQueue<>(k); // 初期容量指定
for (int num : nums) {
add(num);
}
}
public int add(int val) {
topKScores.offer(val);
if (topKScores.size() > k) {
topKScores.poll();
}
return topKScores.peek();
}
}
Owner
Author
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. Ryotaro25/leetcode_first60#66 (comment)
以前このやりとりを目にしていたので、capacityの指定は見送りました。 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. コメントありがとうございます。リンク先の内容、とても勉強になりました。 |
||
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.
せっかくなのでリンク貼っておくといいと思います。
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.
たしかに見返し時に便利ですね。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/PriorityQueue.html