Kth Smallest Element Using Min Heap in DSA Go - Time & Space Complexity
We want to know how the time needed to find the kth smallest number grows as the list gets bigger.
Specifically, how does using a min heap affect the work done as input size changes?
Analyze the time complexity of the following code snippet.
func kthSmallest(nums []int, k int) int {
h := &MinHeap{}
heap.Init(h)
for _, num := range nums {
heap.Push(h, num)
}
var result int
for i := 0; i < k; i++ {
result = heap.Pop(h).(int)
}
return result
}
This code builds a min heap from the input list, then removes the smallest element k times to find the kth smallest.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Inserting all elements into the min heap and popping k times.
- How many times: Insert happens n times (once per element), pop happens k times.
As the list size n grows, building the heap takes more time, and popping k times adds extra work.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 insertions + k pops, each costing log n steps |
| 100 | About 100 insertions + k pops, each costing log 100 steps |
| 1000 | About 1000 insertions + k pops, each costing log 1000 steps |
Pattern observation: The time grows roughly proportional to n times log n, plus k times log n, which is mostly dominated by n log n when k is smaller than n.
Time Complexity: O(n log n + k log n)
This means the time needed grows a bit faster than the size of the list because each insertion and removal takes extra steps related to the heap size.
[X] Wrong: "Building the heap is just O(n) so total time is O(n + k)."
[OK] Correct: While heap construction can be O(n), each pop operation costs O(log n), and pushing all elements individually costs O(n log n), so total time is dominated by these log n steps.
Understanding how heaps work and their time costs helps you explain efficient ways to find order statistics like the kth smallest element, a common real-world problem.
What if we used a max heap of size k instead of a min heap of size n? How would the time complexity change?
Practice
kth smallest element in a list?Solution
Step 1: Understand min heap property
A min heap always keeps the smallest element at the top (root).Step 2: Use min heap to find kth smallest
By extracting the rootktimes, you get thekthsmallest element.Final Answer:
The smallest elements in order -> Option AQuick Check:
Min heap = smallest elements first [OK]
- Confusing min heap with max heap
- Thinking min heap gives largest elements first
- Assuming kth smallest is the middle element
nums using container/heap package?Solution
Step 1: Understand heap.Init usage
heap.Init requires a pointer to a type implementing heap.Interface.Step 2: Convert slice to IntHeap and pass pointer
We must convertnumstoIntHeapand pass its pointer:&IntHeap(nums).Final Answer:
heap.Init(&IntHeap(nums)) -> Option DQuick Check:
heap.Init needs pointer to heap.Interface [OK]
- Passing slice directly without conversion
- Passing value instead of pointer
- Using wrong type conversion syntax
package main
import (
"container/heap"
"fmt"
)
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func main() {
nums := []int{7, 10, 4, 3, 20, 15}
h := IntHeap(nums)
heap.Init(&h)
k := 3
var kthSmallest int
for i := 0; i < k; i++ {
kthSmallest = heap.Pop(&h).(int)
}
fmt.Println(kthSmallest)
}
Solution
Step 1: Build min heap from nums
Heap after init: [3, 7, 4, 10, 20, 15] (smallest at root)Step 2: Pop 3 times to get 3rd smallest
Pop 1: 3, Pop 2: 4, Pop 3: 7Final Answer:
7 -> Option BQuick Check:
3rd smallest in [7,10,4,3,20,15] is 7 [OK]
- Not popping k times
- Confusing min heap with max heap
- Incorrect heap initialization
nums := []int{5, 3, 8, 1}
h := IntHeap(nums)
heap.Init(h)
k := 2
for i := 0; i < k; i++ {
val := heap.Pop(&h).(int)
if i == k-1 {
fmt.Println(val)
}
}Solution
Step 1: Check heap.Init argument
heap.Init requires a pointer to the heap, so it should beheap.Init(&h).Step 2: Confirm other calls are correct
heap.Pop correctly uses&h, andk=2is correct for 2nd smallest.Final Answer:
heap.Init should receive &h, not h -> Option AQuick Check:
heap.Init needs pointer to heap [OK]
- Passing value instead of pointer to heap.Init
- Confusing Pop argument
- Incorrect k value for kth smallest
Solution
Step 1: Understand efficiency of building full min heap
Building a min heap of all elements and popping 5 times is O(n + k log n), which can be costly for large n.Step 2: Use max heap of size k for better efficiency
Maintaining a max heap of size 5 while iterating keeps track of 5 smallest elements efficiently in O(n log k).Step 3: Compare other options
Sorting is O(n log n), popping all elements is inefficient, so max heap size k is best.Final Answer:
Build a max heap of size 5 and iterate through array. -> Option CQuick Check:
Max heap size k = efficient kth smallest [OK]
- Building full min heap for large arrays
- Sorting entire array unnecessarily
- Popping all elements from min heap
Ready to prove it?
Pass the test and this pattern moves unknown
