Skip to content

Commit 77a950e

Browse files
committed
content(blog): max heap
1 parent 56e7adf commit 77a950e

File tree

2 files changed

+210
-0
lines changed

2 files changed

+210
-0
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# Interview Journal: #3 - Max Heap and Min Heap in Golang
2+
3+
In this entry of the Interview Journal, we're diving into **Heaps**. Specifically, how to implement Max Heaps and Min Heaps in Go (Golang). This is a classic interview topic and a fundamental data structure for priority queues, graph algorithms (like Dijkstra's), and efficient sorting.
4+
5+
## What is a Heap?
6+
7+
A **Heap** is a specialized tree-based data structure which is essentially an almost complete tree that satisfies the **heap property**:
8+
9+
* **Max Heap:** For any given node `I`, the value of `I` is greater than or equal to the values of its children. The largest element is at the root.
10+
* **Min Heap:** For any given node `I`, the value of `I` is less than or equal to the values of its children. The smallest element is at the root.
11+
12+
Heaps are usually implemented using arrays (or slices in Go) because they are complete binary trees.
13+
14+
* **Parent Index:** `(i - 1) / 2`
15+
* **Left Child Index:** `2*i + 1`
16+
* **Right Child Index:** `2*i + 2`
17+
18+
### Visualizing a Max Heap
19+
20+
```mermaid
21+
graph TD
22+
root((100))
23+
child1((19))
24+
child2((36))
25+
child1_1((17))
26+
child1_2((3))
27+
child2_1((25))
28+
child2_2((1))
29+
30+
root --- child1
31+
root --- child2
32+
child1 --- child1_1
33+
child1 --- child1_2
34+
child2 --- child2_1
35+
child2 --- child2_2
36+
37+
classDef node fill:#240224,stroke:#333,stroke-width:2px;
38+
class root,child1,child2,child1_1,child1_2,child2_1,child2_2 node;
39+
```
40+
41+
**Array Representation:** `[100, 19, 36, 17, 3, 25, 1]`
42+
43+
## Why do we need Heaps?
44+
45+
Heaps solve a specific problem efficiently: **repeatedly accessing the minimum or maximum element** in a dynamic set of data.
46+
47+
| Data Structure | Find Max | Insert | Remove Max |
48+
| :--- | :--- | :--- | :--- |
49+
| **Unsorted Array** | O(N) | O(1) | O(N) |
50+
| **Sorted Array** | O(1) | O(N) | O(1) |
51+
| **Heap** | **O(1)** | **O(log N)** | **O(log N)** |
52+
53+
**Real-world Use Cases:**
54+
1. **Priority Queues:** Scheduling jobs where "High Priority" tasks run before "Oldest" tasks (e.g., OS process scheduling, bandwidth management).
55+
2. **Graph Algorithms:** Essential for **Dijkstra’s algorithm** (shortest path) and **Prim’s algorithm** (minimum spanning tree).
56+
3. **Heapsort:** An efficient, in-place sorting algorithm with O(N log N) complexity.
57+
58+
## Go's `container/heap`
59+
60+
Go provides a standard library package `container/heap` that defines a heap interface. To use it, your type just needs to implement the `heap.Interface`.
61+
62+
```go
63+
type Interface interface {
64+
sort.Interface // Len, Less, Swap
65+
Push(x any) // add x as element Len()
66+
Pop() any // remove and return element Len() - 1.
67+
}
68+
```
69+
70+
### Implementing a Min Heap
71+
72+
Let's implement a simple `MinHeap` for integers.
73+
74+
```go
75+
package main
76+
77+
import (
78+
"container/heap"
79+
"fmt"
80+
)
81+
82+
// IntHeap is a min-heap of ints.
83+
type IntHeap []int
84+
85+
func (h IntHeap) Len() int { return len(h) }
86+
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } // < for MinHeap
87+
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
88+
89+
func (h *IntHeap) Push(x any) {
90+
*h = append(*h, x.(int))
91+
}
92+
93+
func (h *IntHeap) Pop() any {
94+
old := *h
95+
n := len(old)
96+
x := old[n-1]
97+
*h = old[0 : n-1]
98+
return x
99+
}
100+
101+
func main() {
102+
h := &IntHeap{2, 1, 5}
103+
heap.Init(h)
104+
heap.Push(h, 3)
105+
fmt.Printf("minimum: %d
106+
", (*h)[0]) // 1
107+
108+
for h.Len() > 0 {
109+
fmt.Printf("%d ", heap.Pop(h))
110+
}
111+
// Output: 1 2 3 5
112+
}
113+
```
114+
115+
### Implementing a Max Heap
116+
117+
To turn the above into a `MaxHeap`, we only need to change the `Less` function.
118+
119+
```go
120+
// For MaxHeap, we want the larger element to come "first" (be the root)
121+
func (h IntHeap) Less(i, j int) bool { return h[i] > h[j] } // > for MaxHeap
122+
```
123+
124+
Alternatively, if you are just dealing with numbers, you can store negative values in a Min Heap to simulate a Max Heap, but implementing the interface is cleaner.
125+
126+
## From Scratch (For Interviews)
127+
128+
Sometimes interviewers ask you to implement `push` and `pop` logic without using the library. This tests your understanding of **Bubbling Up (Heapify Up)** and **Bubbling Down (Heapify Down)**.
129+
130+
### Heapify Up (Push)
131+
132+
When we add a new element, we append it to the end of the array. Then we check if it violates the heap property with its parent. If it does, we swap them. We repeat this until the property is restored or we reach the root.
133+
134+
```go
135+
func (h *MaxHeap) Push(val int) {
136+
h.slice = append(h.slice, val)
137+
h.heapifyUp(len(h.slice) - 1)
138+
}
139+
140+
func (h *MaxHeap) heapifyUp(index int) {
141+
for h.slice[parent(index)] < h.slice[index] {
142+
h.swap(parent(index), index)
143+
index = parent(index)
144+
}
145+
}
146+
```
147+
148+
### Heapify Down (Pop)
149+
150+
When we remove the root (max/min), we take the *last* element in the array and put it at the root. Then we compare it with its children. If it violates the heap property, we swap it with the larger (or smaller for min-heap) of the two children. Repeat until the property is restored or we reach a leaf.
151+
152+
```go
153+
func (h *MaxHeap) Pop() int {
154+
max := h.slice[0]
155+
last := len(h.slice) - 1
156+
h.slice[0] = h.slice[last]
157+
h.slice = h.slice[:last]
158+
h.heapifyDown(0)
159+
return max
160+
}
161+
162+
func (h *MaxHeap) heapifyDown(index int) {
163+
lastIndex := len(h.slice) - 1
164+
l, r := left(index), right(index)
165+
childToCompare := 0
166+
167+
for l <= lastIndex {
168+
if l == lastIndex { // only left child
169+
childToCompare = l
170+
} else if h.slice[l] > h.slice[r] { // left is larger
171+
childToCompare = l
172+
} else { // right is larger
173+
childToCompare = r
174+
}
175+
176+
if h.slice[index] < h.slice[childToCompare] {
177+
h.swap(index, childToCompare)
178+
index = childToCompare
179+
l, r = left(index), right(index)
180+
} else {
181+
return
182+
}
183+
}
184+
}
185+
```
186+
187+
## Time Complexity
188+
189+
| Operation | Time Complexity |
190+
| :--- | :--- |
191+
| **Push** | O(log N) |
192+
| **Pop** | O(log N) |
193+
| **Peek (Top)** | O(1) |
194+
| **Build Heap** | O(N) |
195+
196+
## Summary
197+
198+
* Use `container/heap` for production code.
199+
* Remember `Less(i, j)` determines the order. `h[i] < h[j]` is Min Heap. `h[i] > h[j]` is Max Heap.
200+
* Understand the array indices math: `2*i+1`, `2*i+2`, `(i-1)/2`.
201+
* "Bubble Up" for insertion, "Bubble Down" for deletion.

public/posts/posts.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,15 @@
11751175
"category": "dev",
11761176
"tags": ["cpp", "cplusplus", "memory-management", "rule-of-five", "interview", "dev"],
11771177
"authors": ["fezcode"]
1178+
},
1179+
{
1180+
"slug": "max-heap-min-heap-golang",
1181+
"title": "Interview Journal: #3 - Max Heap and Min Heap in Golang",
1182+
"filename": "/interview-journal/03-max-heap-min-heap-golang.txt",
1183+
"date": "2026-02-13",
1184+
"category": "dev",
1185+
"tags": ["golang", "go", "heap", "data-structures", "interview", "dev"],
1186+
"authors": ["fezcode"]
11781187
}
11791188
]
11801189
},

0 commit comments

Comments
 (0)