CSCI 1933 – Introduction to Algorithms and Data Structures Adriana Picoral
Typically, some type of ordering will be imposed on the elements in a binary tree.
Binary Search Tree (BST)
A BST has a rule about left vs. right children/subtree
A BST gives you ordered data – which is great if you need sorted data
BSTs are great if you need to search the data structure often (balanced would be better)
Weakness: to find the minimum or maximum, you have to traverse all the way to a leaf \(O(\log n)\) (for not balanced trees, \(O(n)\))
What if that’s the only operation you care about — always getting the min (or max) quickly?
Heap
What if that’s the only operation you care about — always getting the min (or max) quickly?
For example, a priority queue is a data structure where each element is associated with a priority, and elements with higher priority are served before those with lower priority.
That’s the problem a heap solves.
Heap
The heap property is simpler than BST:
A BST has a rule about left vs. right children
A heap has a simpler rule about parent vs. child:
Min-heap: every parent is ≤ its children → the root is always the minimum
Max-heap: every parent is ≥ its children → the root is always the maximum
Heap
A heap has a simpler rule about parent vs. child:
Min-heap: every parent is ≤ its children → the root is always the minimum
Max-heap: every parent is ≥ its children → the root is always the maximum
No ordering between siblings, no left-vs-right rule. This makes heaps less “sorted” than a BST, but much faster for one specific job: always knowing the min/max in O(1)
Min-heap
Every parent is ≤ its children → the root is always the minimum
Max-heap
Every parent is ≥ its children → the root is always the maximum
Heap
The shape rule:
A heap is always a complete binary tree — every level is filled left to right, with no gaps.
This is stricter than a BST in terms of shape.
Why does this matter? Because a complete binary tree maps perfectly onto an array
Key: array-based implementation of a complete binary tree
In-class exercise
Go to gradescope and answer the heap or not heap exercise
Heap – array implementation
A complete binary tree maps perfectly onto an array:
10
/ \
15 20
/ \
30 40
Index: 0 1 2 3 4
Value: [10, 15, 20, 30, 40]
No pointers needed. This is why heaps are almost always implemented as arrays, not as linked nodes — it’s faster and uses less memory.
Heap – array implementation
Index: 0 1 2 3 4
Value: [10, 15, 20, 30, 40]
For a node at index i:
Left child → 2i + 1
Right child → 2i + 2
Parent → (i - 1) / 2
Heap – array implementation
Index: 0 1 2 3 4
Value: [10, 15, 20, 30, 40]
Left child of 15 is at \(2 * 1 + 1 = 3\)
Heap – operations
Two core operations:
insert
get min/max
Both operations are O(log n) because the tree is always complete — it never gets unbalanced like a BST can.
Heap – insert
Add at the end, then “bubble up” (heapify):
Place the new value at the next open spot (end of array)
Compare with its parent; if it violates the heap property, swap
Repeat until the property is restored
Heap – insert
Let’s insert 2 in our min heap:
Place the new value at the next open spot (end of array)
Index: 0 1 2 3 4 5
Value: [10, 15, 20, 30, 40, 2]
Heap – insert
Let’s insert 2 in our min heap:
Place the new value at the next open spot (end of array)
Heap – insert
Let’s insert 2 in our min heap:
Compare with its parent; if it violates the heap property, swap
Repeat until the property is restored
Index: 0 1 2 3 4 5
Value: [10, 15, 20, 30, 40, 2]
Parent is at \((i - 1) / 2\):
parent of index 5 is at index 2, swap
Index: 0 1 2 3 4 5
Value: [10, 15, 2, 30, 40, 20]
Heap – insert
Compare with its parent; if it violates the heap property, swap
Repeat until the property is restored
Index: 0 1 2 3 4 5
Value: [10, 15, 2, 30, 40, 20]
Parent is at \((i - 1) / 2\):
parent of index 2 is at index 0, swap
Index: 0 1 2 3 4 5
Value: [2, 15, 10, 30, 40, 20]
Result
Index: 0 1 2 3 4 5
Value: [2, 15, 10, 30, 40, 20]
Heap – min/max (deletion)
Take the root, then “bubble down”:
Grab the root (that’s your min/max)
Move the last element to the root position
Compare with children; swap with the smaller child if needed
Repeat until the property is restored
Heap – min/max (deletion)
Index: 0 1 2 3 4 5
Value: [2, 15, 10, 30, 40, 20]
Grab the root (that’s your min/max)
Move the last element to the root position
Index: 0 1 2 3 4
Value: [20, 15, 10, 30, 40]
Heap – min/max (deletion)
Grab the root (that’s your min/max)
Move the last element to the root position
Index: 0 1 2 3 4
Value: [20, 15, 10, 30, 40]
Heap – min/max (deletion)
Compare with children; swap with the smaller child if needed
Repeat until the property is restored
Index: 0 1 2 3 4
Value: [20, 15, 10, 30, 40]
Children of root are at indices 1 and 2
Swap value at index 2 with root
Index: 0 1 2 3 4
Value: [10, 15, 20, 30, 40]
Heap – min/max (deletion)
Compare with children; swap with the smaller child if needed
Repeat until the property is restored
Index: 0 1 2 3 4
Value: [10, 15, 20, 30, 40]
Children of index 2 are at indices 5 and 6 (nothing there)
public interface Heap<T> { void insert(T value); T pop(); T peek(); boolean isEmpty(); int length();}
MinHeap – implementation
public class MinHeap<T extends Comparable<T>> implements Heap<T> { private T[] data; private int length; private int size; @SuppressWarnings("unchecked") public MinHeap(int size) { data = (T[]) new Comparable[size]; // Comparable, not Object this.size = size; this.length = 0; }}
MinHeap – implementation
Here are the extra methods we need to implement in MinHeap (besides all methods in the Heap interface):
Methods to get indices of left and right child, and parent:
private int parent(int i) { return (i - 1) / 2; }
private int leftChild(int i) { return 2 * i + 1; }
private int rightChild(int i) { return 2 * i + 2; }
MinHeap – implementation
Here are the extra methods we need to implement in MinHeap (besides all methods in the Heap interface):
Methods to check if there is a parent, a left or right child:
private boolean hasParent(int i) { return i > 0; }
private void bubbleUp(int i) { while (hasParent(i) && data[i].compareTo(data[parent(i)]) < 0) { swap(i, parent(i)); i = parent(i); }}
MinHeap – toString
public String toString() { String out = "[ "; for (int i = 0; i < length; i++) out += data[i] + " "; return out + "]";}
MinHeap – insert
public void insert(T value) { if (length == data.length) throw new IllegalStateException("Heap is full"); data[length] = value; // place at the end length++; bubbleUp(length - 1); // heapify}
MinHeap – implementation
Let’s implement T pop()
Check if heap is empty (throw error if that’s the case)
Grab root value (at index zero)
Move last element in array to root (index zero)
Update length variable (we have one fewer elements in the heap)
Heapify – bubble down (find the smaller of the two children, swap with smallest child)
Submit your Heap.java and MinHeap.java to gradescope
MinHeap – implementation
Submit your Heap.java and MinHeap.java to gradescope
public static void main(String[] args) { MinHeap<Integer> heap = new MinHeap<>(10); heap.insert(40); heap.insert(15); heap.insert(30); heap.insert(5); heap.insert(10); heap.insert(20); heap.insert(0); heap.insert(2); System.out.print("After inserts: "); System.out.println(heap); System.out.println("\nRemoving in order:"); while (!heap.isEmpty()) { System.out.println(heap.pop()); } }
MinHeap – bubbleDown
private void bubbleDown(int i) { while (hasLeftChild(i)) { int smallerChild = leftChild(i); // find the smaller of the two children if (hasRightChild(i) && data[rightChild(i)].compareTo(data[leftChild(i)]) < 0) { smallerChild = rightChild(i); } if (data[i].compareTo(data[smallerChild]) <= 0) return; swap(i, smallerChild); i = smallerChild; }}
MinHeap – pop
public T pop() { if (isEmpty()) throw new IllegalStateException("Heap is empty"); T min = data[0]; data[0] = data[length - 1]; // move last element to root length--; bubbleDown(0); // heapify return min;}
Looking back
A BST is a Swiss Army knife — it can do many things decently.
A heap is a scalpel — it does one thing (priority access) extremely well.