Merge K Sorted Lists Using Min Heap in DSA Go - Time & Space Complexity
We want to know how long it takes to merge multiple sorted lists using a min heap.
Specifically, how does the time grow as the number of lists and their sizes increase?
Analyze the time complexity of the following code snippet.
import "container/heap"
type Item struct {
value int
listIndex int
elementIndex int
}
type IntHeap []*Item
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i].value < h[j].value }
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.(*Item))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
item := old[n-1]
*h = old[0 : n-1]
return item
}
func mergeKLists(lists [][]int) []int {
minHeap := &IntHeap{}
heap.Init(minHeap)
for i, list := range lists {
if len(list) > 0 {
heap.Push(minHeap, &Item{value: list[0], listIndex: i, elementIndex: 0})
}
}
var result []int
for minHeap.Len() > 0 {
item := heap.Pop(minHeap).(*Item)
result = append(result, item.value)
if item.elementIndex+1 < len(lists[item.listIndex]) {
nextIndex := item.elementIndex + 1
heap.Push(minHeap, &Item{value: lists[item.listIndex][nextIndex], listIndex: item.listIndex, elementIndex: nextIndex})
}
}
return result
}
This code merges k sorted lists into one sorted list using a min heap to always pick the smallest next element.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Removing the smallest element from the heap and adding the next element from the same list.
- How many times: This happens once for every element across all lists, so total n times where n is the total number of elements.
Each element is pushed and popped from the heap once. The heap size is at most k (number of lists).
| Input Size (n) | Approx. Operations |
|---|---|
| 10 (k=3) | About 10 push/pop operations on heap of size ≤ 3 |
| 100 (k=5) | About 100 push/pop operations on heap of size ≤ 5 |
| 1000 (k=10) | About 1000 push/pop operations on heap of size ≤ 10 |
Pattern observation: The number of operations grows linearly with total elements, but each heap operation depends on k, which is usually much smaller than n.
Time Complexity: O(n log k)
This means the time grows mostly with the total number of elements n, but each step involves a quick operation on a heap of size k.
[X] Wrong: "The time complexity is O(n log n) because we use a heap for all elements."
[OK] Correct: The heap never holds all n elements at once, only up to k elements, so the log factor depends on k, not n.
Understanding this time complexity helps you explain efficient merging of sorted data streams, a common real-world problem.
"What if we used a simple list instead of a min heap to find the smallest element each time? How would the time complexity change?"
Practice
Solution
Step 1: Understand the role of a min heap in merging
A min heap keeps track of the smallest elements efficiently, allowing quick access to the smallest head among K lists.Step 2: Compare other options
Options B, C, and D do not describe the min heap's role correctly. Sorting individually or concatenating without sorting does not merge sorted lists efficiently.Final Answer:
It efficiently finds the smallest element among the heads of all lists. -> Option CQuick Check:
Min heap = efficient smallest element access [OK]
- Thinking min heap sorts entire lists
- Assuming min heap stores all elements at once
- Confusing min heap with simple concatenation
Solution
Step 1: Identify Go's min heap implementation
Go uses slices combined with the container/heap package to implement min heaps efficiently.Step 2: Evaluate other options
Maps and linked lists do not provide heap properties. Arrays without heap functions lack automatic heap behavior.Final Answer:
slice with container/heap package -> Option DQuick Check:
Go min heap = slice + container/heap [OK]
- Using map which has no order
- Using linked list which is slow for heap ops
- Ignoring container/heap package
list1 = [1, 4, 7] list2 = [2, 5, 8] list3 = [3, 6, 9]
Solution
Step 1: Understand merging with min heap
The min heap picks the smallest head element from all lists repeatedly, producing a fully sorted merged list.Step 2: Merge given lists step-by-step
Starting with heads: 1, 2, 3; pick 1, then 2, then 3, then 4, 5, 6, 7, 8, 9 in order.Final Answer:
[1, 2, 3, 4, 5, 6, 7, 8, 9] -> Option BQuick Check:
Merge sorted lists = sorted combined list [OK]
- Concatenating lists without sorting
- Reversing order incorrectly
- Mixing elements from different lists randomly
func mergeKLists(lists [][]int) []int {
var result []int
h := &IntHeap{}
heap.Init(h)
for _, list := range lists {
for _, val := range list {
heap.Push(h, val)
}
}
for h.Len() > 0 {
result = append(result, heap.Pop(h).(int))
}
return result
}Solution
Step 1: Analyze the merging approach
The code pushes all elements from all lists into the heap at once, ignoring the sorted order advantage.Step 2: Understand the impact
This approach works but is inefficient (O(N log N)) instead of O(N log K) by pushing only heads and updating heap dynamically.Final Answer:
Pushing all elements at once loses the benefit of merging sorted lists. -> Option AQuick Check:
Push all elements = inefficient merge [OK]
- Ignoring heap.Init call
- Misunderstanding result slice declaration
- Incorrect type assertion on Pop
Solution
Step 1: Use min heap with heads of each list
Push only the first node of each list into the min heap to keep track of the smallest current element among lists.Step 2: Pop smallest and push next node
Pop the smallest node, add it to the merged list, then push the next node from that list into the heap, repeating until all nodes are processed.Step 3: Time complexity advantage
This approach runs in O(N log K) time, where N is total nodes and K is number of lists, which is efficient.Final Answer:
Push the first node of each list into the min heap, then repeatedly pop the smallest and push its next node. -> Option AQuick Check:
Push heads + pop/push next = efficient merge [OK]
- Pushing all nodes at once causing inefficiency
- Using max heap which gives largest element first
- Sorting concatenated list which is slower
Ready to prove it?
Pass the test and this pattern moves unknown
