ヒープで 流れてくる 点数の k 番目に 高い値を すぐ 答える 仕組みを作る
点数が
海外で定番の問題リスト 4 本には入っていません
問題
ゲームのKthLargest として
- 作る
ときに、 整数 kと、それまでの 点数の 並び numsを受け取ります add(val)は、点数 valを 1 つ足し、それまでに 届いた 全部の 点数 ( numsを含む) を 高い 順に 並べた とき前から k 番目に 来る 値を返します - 同じ
点数も それぞれ 1 つと 数えます。 [9, 9, 7]で 2 番目に高い値は 9 です addを呼んだ 時点で、 点数は 必ず k 個以上あります kは 1 以上 10000 以下、numsの長さは 0 以上 10000 以下、 点数は どれも -10000 以上 10000 以下です。 addは 10000 回まで呼びますaddの 1 回は O(log k) でこなしてください
例のops にargs にnull です。
入力と出力の例
例 1
k は 2 です。
例 2
k は 1 で、
例 3
同じ
面接での聞かれ方
面接では、
- 「数が
流れてくる たびに、 k 番目に 大きい値を 返すクラスを 作ってください」 - 書いた
あとに 「add の 1 回は どれだけかかりますか? 全部を 持つ必要は ありますか?」と 聞かれます - 「k 番目に
小さい 値だったら、 ヒープの 向きは どうしますか?」と 裏返して 聞かれる こともあります
同じ値をadd の
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「ハッシュマップ」に、
関わる学び
ハッシュは、
まず 考える手順
段 1
まずはadd のadd で O(n log n) かかり、
段 2
k 番目に
段 3
上位の k 個の
段 4
作るnums も、add のnums が k 個よりadd で k 個に
解答
Python
Python
from heapq import heappop
from heapq import heappush
class KthLargest:
def __init__(self, k, nums):
self.k = k
self.h = []
for x in nums:
self.add(x)
def add(self, val):
heappush(self.h, val)
if len(self.h) > self.k:
heappop(self.h)
return self.h[0]JavaScript
JavaScript
class KthLargest {
constructor(k, nums) {
this.k = k;
this.h = [];
for (const x of nums) {
this.add(x);
}
}
add(val) {
const h = this.h;
h.push(val);
up(h, h.length - 1);
if (h.length > this.k) {
h[0] = h.pop();
down(h, 0);
}
return h[0];
}
}
function up(h, i) {
while (i > 0) {
const p = (i - 1) >> 1;
if (h[p] <= h[i]) {
return;
}
[h[p], h[i]] = [h[i], h[p]];
i = p;
}
}
function down(h, i) {
const n = h.length;
while (true) {
let s = i;
const l = 2 * i + 1;
const r = l + 1;
if (l < n && h[l] < h[s]) {
s = l;
}
if (r < n && h[r] < h[s]) {
s = r;
}
if (s === i) {
return;
}
[h[s], h[i]] = [h[i], h[s]];
i = s;
}
}Go
Go
import "container/heap"
type minHeap []int
func (h minHeap) Len() int {
return len(h)
}
func (h minHeap) Less(
i, j int,
) bool {
return h[i] < h[j]
}
func (h minHeap) Swap(
i, j int,
) {
h[i], h[j] = h[j], h[i]
}
func (h *minHeap) Push(x any) {
*h = append(*h, x.(int))
}
func (h *minHeap) Pop() any {
old := *h
x := old[len(old)-1]
*h = old[:len(old)-1]
return x
}
type KthLargest struct {
k int
h *minHeap
}
func newKthLargest(
k int, nums []int,
) *KthLargest {
s := &KthLargest{
k: k, h: &minHeap{}}
for _, x := range nums {
s.Add(x)
}
return s
}
func (s *KthLargest) Add(
val int,
) int {
heap.Push(s.h, val)
if s.h.Len() > s.k {
heap.Pop(s.h)
}
return (*s.h)[0]
}JavaScript でpop してpop の
計算量
addの 1 回は O(log k) です。ヒープの 大きさは k + 1 までで、 足すのと 取り出すのが それぞれ O(log k) です - 作る
ときは、 numsの長さを n として O(n log k) です - 使う
記憶は O(k) です。 上位の k 個だけを持ちます
よく ある間違い
- 大きい順の
ヒープに 全部入れる。 根が いちばん高い 値に なり、 k 番目を 取るには k 回取り出して 戻す必要が あります。 1 回が O(k log n) に なり、 記憶も 全部の 点数ぶん使います - 捨てた点数を
あとで 戻そうとする。 上位 k 個から 外れた 点数は、 点数が 増えるだけの この 問題では 二度と k 番目になりません。 持たなくてかまいません - 作る
ときに numsを全部入れたまま、 大きさを k に 削らない。 次の addで 1つしか 取り出さないので、 根が k 番目になりません。 作る ときも addと同じ 手順で入れます - 同じ
点数を 1 つに まとめる。 例 3 で 集合を 使うと 5 が 1 つに なり、 3 番目が 無くなります
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「k 番目に
高い値を 決めるのに 要るのは 上位の k 個だけで、 そこから 落ちた値は もう 戻ってきません。 なので 上位の k 個を 小さい 順の ヒープに 持ちます。 足して 大きさが k を 超えたら 根を 捨て、 残った 根を 返します。 作る ときの nums も 同じ add で 入れます。 add の 1 回は O(log k)、 記憶は O(k) です。」
書き終えたら、
「例 1 で k は 2 です。
6、 1、 9 を 入れると 1 が 捨てられ、 ヒープは 6 と 9 です。 4 を 足すと 4 が 捨てられ、 根の 6 を 返します。 7 を 足すと 6 が 捨てられて根は 7、 2 は 捨てられて 7 のまま、 12 を 足すと 7 が 捨てられて根の 9 を 返します。」