Maintain the kth largest value in a dynamic stream by keeping a min-heap of exactly k elements. The heap's root is always the smallest of the top-k values — exactly the kth largest seen so far.
Design a class that tracks the kth largest element in a live stream of integers. constructor(k, nums) seeds it with an initial array, and add(val) inserts a new value and returns the current kth largest.
Worked example: k = 3, nums = [4, 5, 8, 2]. After construction the top-3 are [4, 5, 8]; the 3rd largest is 4. Calling add(3) → top-3 become [3, 4, 5] (8 stays above, 2 is still outside), so it returns 3. Calling add(5) → top-3 become [4, 5, 5], returns 4.
add pushes the new value, then pops the root if the heap exceeds size k (evicting the smallest). The root is then the answer.k, then call add() for every element in nums — so construction reuses the same logic.O(log k)).heap.length > k, pop the minimum (the value that just fell out of the top-k). This keeps the heap at exactly size k.heap[0] is the minimum of the k-heap, i.e. the kth largest overall.Constructor costs O(n log k) for seeding with n initial values. Space is O(k) — only k elements ever live in the heap.
k = 3 and seed array [4, 5, 8, 2]. We'll build a min-heap of exactly 3 elements — the root is always the kth largest seen so far.1▶class KthLargest {2▶ private heap: number[] = [];3▶ private k: number;45▶ constructor(k: number, nums: number[]) {6▶ this.k = k;7 for (const n of nums) this.add(n);8 }910 add(val: number): number {11 this._heapPush(val);12 if (this.heap.length > this.k) this._heapPop();13 return this.heap[0]; // min of the k-heap = kth largest overall14 }1516 // --- min-heap helpers ---17 private _heapPush(val: number): void {18 this.heap.push(val);19 let i = this.heap.length - 1;20 while (i > 0) {21 const parent = (i - 1) >> 1;22 if (this.heap[parent] <= this.heap[i]) break;23 [this.heap[parent], this.heap[i]] = [this.heap[i], this.heap[parent]];24 i = parent;25 }26 }2728 private _heapPop(): number {29 const top = this.heap[0];30 const last = this.heap.pop()!;31 if (this.heap.length > 0) {32 this.heap[0] = last;33 let i = 0;34 while (true) {35 const l = 2 * i + 1, r = 2 * i + 2;36 let smallest = i;37 if (l < this.heap.length && this.heap[l] < this.heap[smallest]) smallest = l;38 if (r < this.heap.length && this.heap[r] < this.heap[smallest]) smallest = r;39 if (smallest === i) break;40 [this.heap[smallest], this.heap[i]] = [this.heap[i], this.heap[smallest]];41 i = smallest;42 }43 }44 return top;45 }46}
class KthLargest {
private heap: number[] = [];
private k: number;
constructor(k: number, nums: number[]) {
this.k = k;
for (const n of nums) this.add(n);
}
add(val: number): number {
this._heapPush(val);
if (this.heap.length > this.k) this._heapPop();
return this.heap[0]; // min of the k-heap = kth largest overall
}
// --- min-heap helpers ---
private _heapPush(val: number): void {
this.heap.push(val);
let i = this.heap.length - 1;
while (i > 0) {
const parent = (i - 1) >> 1;
if (this.heap[parent] <= this.heap[i]) break;
[this.heap[parent], this.heap[i]] = [this.heap[i], this.heap[parent]];
i = parent;
}
}
private _heapPop(): number {
const top = this.heap[0];
const last = this.heap.pop()!;
if (this.heap.length > 0) {
this.heap[0] = last;
let i = 0;
while (true) {
const l = 2 * i + 1, r = 2 * i + 2;
let smallest = i;
if (l < this.heap.length && this.heap[l] < this.heap[smallest]) smallest = l;
if (r < this.heap.length && this.heap[r] < this.heap[smallest]) smallest = r;
if (smallest === i) break;
[this.heap[smallest], this.heap[i]] = [this.heap[i], this.heap[smallest]];
i = smallest;
}
}
return top;
}
}heap is a plain array managed as a min-heap; k is stored so every add knows the cap. No library needed — we implement push/pop inline.k, then delegate each seed element to add(). This reuses the trimming logic rather than duplicating it, and means the heap is never larger than k even after seeding.heap[0] — the smallest of those k values — which is the kth largest overall.>> bit-shift is an efficient way to compute (i - 1) / 2 | 0.add call does one push + at most one pop, both O(log k). The constructor seeds with n elements → total O(n log k). Space is O(k).add still just compares and possibly replaces.heap.slice() gives all k elements but in heap order, not sorted; sort them additionally in O(k log k) if needed.add populates it. LeetCode guarantees at least one call toadd before reading the result.class KthLargest {
private heap: number[] = [];
private k: number;
constructor(k: number, nums: number[]) {
this.k = k;
for (const n of nums) this.add(n);
}
add(val: number): number {
this._heapPush(val);
if (this.heap.length > this.k) this._heapPop();
return this.heap[0]; // min of the k-heap = kth largest overall
}
// --- min-heap helpers ---
private _heapPush(val: number): void {
this.heap.push(val);
let i = this.heap.length - 1;
while (i > 0) {
const parent = (i - 1) >> 1;
if (this.heap[parent] <= this.heap[i]) break;
[this.heap[parent], this.heap[i]] = [this.heap[i], this.heap[parent]];
i = parent;
}
}
private _heapPop(): number {
const top = this.heap[0];
const last = this.heap.pop()!;
if (this.heap.length > 0) {
this.heap[0] = last;
let i = 0;
while (true) {
const l = 2 * i + 1, r = 2 * i + 2;
let smallest = i;
if (l < this.heap.length && this.heap[l] < this.heap[smallest]) smallest = l;
if (r < this.heap.length && this.heap[r] < this.heap[smallest]) smallest = r;
if (smallest === i) break;
[this.heap[smallest], this.heap[i]] = [this.heap[i], this.heap[smallest]];
i = smallest;
}
}
return top;
}
}add call does one push + at most one pop, both O(log k). The constructor seeds with n elements → total O(n log k). Space is O(k).Keep every value ever seen in an array. On each add, push the new value, sort the whole array descending, and read off index k - 1.
class KthLargest {
private nums: number[];
private k: number;
constructor(k: number, nums: number[]) {
this.k = k;
this.nums = [...nums];
}
add(val: number): number {
this.nums.push(val);
this.nums.sort((a, b) => b - a); // descending
return this.nums[this.k - 1]; // kth largest
}
}add and stores all n values. A size-k min-heap keeps only the top k and answers each add in O(log k) time, O(k) space.| "kth largest / smallest in a stream" | bounded min/max-heap of size k |
| top-k elements, online / dynamic | min-heap, pop when size > k |
| "median from data stream" | two heaps: max-heap + min-heap |
| k closest / most frequent elements | heap keyed on distance/frequency |
class KthLargest {
private heap: number[] = [];
private k: number;
constructor(k: number, nums: number[]) {
this.k = k;
for (const n of nums) this.add(n);
}
add(val: number): number {
// push onto min-heap
// if size > k, pop the smallest (it's outside the top-k)
// heap[0] is the kth largest
return this.heap[0];
}
}k = 3 and stream so far [4, 5, 8, 2], what does add(3) return?heap.length > k after a push?k = 2, after seeding with [1, 10] and calling add(5), what is in the heap and what is returned?