Stream add(val) calls · each one's return value below it
minHeap tree · every parent ≤ its children
Popped for good
minHeap list children of i sit at 2i + 1 and 2i + 2
class KthLargest { private final PriorityQueue<Integer> minHeap; private final int k; public KthLargest(int k, int[] nums) { this.k = k; List<Integer> list = new ArrayList<>(); for (int n : nums) { list.add(n); } minHeap = new PriorityQueue<>(list); // heapify while (minHeap.size() > k) { minHeap.poll(); } } public int add(int val) { minHeap.offer(val); if (minHeap.size() > k) { minHeap.poll(); } return minHeap.peek(); }}
- k
- —
- val
- —
- minHeap.size()
- 0
- minHeap
- []
- returned
- []
Why it works
- Brute force keeps everything sorted: each add inserts into a sorted list (O(n) shifting) or re-sorts (O(n log n)), and memory grows with the whole stream.
- Only the top k matter. Keep exactly the k largest values in a min-heap. The smallest of them sits at the root, and that is the k-th largest overall. Anything below the root can never be the answer again, so pop it.
- Push, then pop if too big: the heap briefly holds k + 1 values, and
poll()throws out the smallest. Each add is O(log k); the constructor is an O(n) heapify (thePriorityQueue(Collection)constructor) plus (n − k) polls. Space O(k). - How the heap moves: push appends a leaf and sifts it up past larger parents; pop moves the last leaf to the root and sifts it down past its smaller child. At most log₂(size) swaps each.
- Pitfalls: the constructor needs
while, notif, because nums can be much longer than k. A max-heap is the wrong tool: its root is the largest, not the k-th largest. - Follow-up: 215 Kth Largest Element in an Array uses the same size-k heap, or quickselect for O(n) average. 347 Top K Frequent Elements is the same pattern keyed by count.