Heap

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

BST 10 10 15 15 10--15 20 20 10--20 30 30 15--30 40 40 15--40

Max-heap

Every parent is ≥ its children → the root is always the maximum

BST 80 80 75 75 80--75 62 62 80--62 51 51 75--51 42 42 75--42

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]

BST 10 10 15 15 10--15 20 20 10--20 30 30 15--30 40 40 15--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):

  1. Place the new value at the next open spot (end of array)
  2. Compare with its parent; if it violates the heap property, swap
  3. Repeat until the property is restored

Heap – insert

Let’s insert 2 in our min heap:

  1. 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:

  1. Place the new value at the next open spot (end of array)

BST 10 10 15 15 10--15 20 20 10--20 30 30 15--30 40 40 15--40 2 2 20--2 null 20--null

Heap – insert

Let’s insert 2 in our min heap:

  1. Compare with its parent; if it violates the heap property, swap
  2. 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

  1. Compare with its parent; if it violates the heap property, swap
  2. 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]

BST 2 2 15 15 2--15 10 10 2--10 30 30 15--30 40 40 15--40 20 20 10--20 null 10--null

Heap – min/max (deletion)

Take the root, then “bubble down”:

  1. Grab the root (that’s your min/max)
  2. Move the last element to the root position
  3. Compare with children; swap with the smaller child if needed
  4. Repeat until the property is restored

Heap – min/max (deletion)

Index:   0   1  2   3   4  5
Value: [2, 15, 10, 30, 40, 20]
  1. Grab the root (that’s your min/max)
  2. Move the last element to the root position
Index:   0   1  2   3   4  
Value: [20, 15, 10, 30, 40]

Heap – min/max (deletion)

  1. Grab the root (that’s your min/max)
  2. Move the last element to the root position
Index:   0   1  2   3   4  
Value: [20, 15, 10, 30, 40]

BST 20 20 15 15 20--15 10 10 20--10 30 30 15--30 40 40 15--40

Heap – min/max (deletion)

  1. Compare with children; swap with the smaller child if needed
  2. 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)

  1. Compare with children; swap with the smaller child if needed
  2. 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)

Heap – min/max (deletion) – final state

Index:   0   1  2   3   4  
Value: [10, 15, 20, 30, 40]

BST 10 10 15 15 10--15 20 20 10--20 30 30 15--30 40 40 15--40

Heap vs. BST runtime

  ┌────────────────────┬──────────────────────────────┬────────────────────┐
  │                    │             BST              │        Heap        │
  ├────────────────────┼──────────────────────────────┼────────────────────┤  
  │ Find min/max       │ O(log n) average, O(n) worst │ O(1) always        │
  ├────────────────────┼──────────────────────────────┼────────────────────┤
  │ Insert             │ O(log n) average, O(n) worst │ O(log n) always    │
  ├────────────────────┼──────────────────────────────┼────────────────────┤
  │ Search for a value │ O(log n) average             │ O(n) — not its job │
  ├────────────────────┼──────────────────────────────┼────────────────────┤ 
  │ Implementation     │ Linked nodes                 │ Array              │
  ├────────────────────┼──────────────────────────────┼────────────────────┤ 
  │ Shape guarantee    │ None                         │ Always complete    │
  └────────────────────┴──────────────────────────────┴────────────────────┘  

Heap – interface

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 boolean hasLeftChild(int i) { return leftChild(i) < length; }
    • private boolean hasRightChild(int i) { return rightChild(i) < length; }

MinHeap – implementation

Here are the extra methods we need to implement in MinHeap (besides all methods in the Heap interface):

private void swap(int a, int b) {
  T temp = data[a];
  data[a] = data[b];
  data[b] = temp;
}

MinHeap – implementation

Let’s implement insert(T value)

  1. Do something about length reaching size (can throw error, or expand array)
  2. Place value argument at the next open spot (length is the index for the next open spot)
  3. Update length variable (we have one more element in our heap)
  4. Heapify (bubble up, compare inserted value with its parent, swapping if inserted value is smaller than parent)

Implement toString() to check your insertion. Submit your Heap.java and MinHeap.java to gradescope

MinHeap – implementation

Implement toString() to check your insertion. 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.println(heap);
    }

MinHeap – bubbleUp

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()

  1. Check if heap is empty (throw error if that’s the case)
  2. Grab root value (at index zero)
  3. Move last element in array to root (index zero)
  4. Update length variable (we have one fewer elements in the heap)
  5. 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.