Max Heap

A Max Heap is a specialized tree-based data structure that is heavily used to implement Priority Queues. It is always a complete binary tree, meaning every level is fully filled except possibly the last one, which is filled strictly from left to right.

The Heap Property:
In a Max Heap, the value of any given node is always greater than or equal to the values of its children. This guarantees that the absolute largest value in the structure is always positioned at the Root.

The Array Magic

Because a Heap is a complete tree, we do not need to create Node objects with "left" and "right" pointers in memory. We can flatten the entire tree into a standard Array. The relationships are calculated using simple math on the indices.

If a parent node is located at index $i$:

Core Operations

1. Insertion (Bubble Up / Sift Up)

When you insert a new value, it is first added to the very end of the array (the bottom-right of the tree). Then, it compares itself to its parent. If it is larger, they swap. This "bubbling up" continues until the Heap Property is restored.

def insert(heap, value):
    heap.append(value)
    index = len(heap) - 1
    
    # Bubble Up
    while index > 0:
        parent = (index - 1) // 2
        if heap[index] > heap[parent]:
            heap[index], heap[parent] = heap[parent], heap[index]
            index = parent
        else:
            break

2. Extract Max (Bubble Down / Sift Down)

Removing the maximum value (the Root) creates a hole at the top of the tree. To fix this:

3. Arbitrary Deletion

Deleting a specific value anywhere in the heap combines both mechanics. First, we must search for the value and swap it with the last element in the array. Once the last element takes the target's place, it might violate the heap property in either direction!

def delete_value(heap, value):
    try:
        index = heap.index(value) # O(n) search
    except ValueError:
        return # Value not found

    last_index = len(heap) - 1
    if index != last_index:
        # Swap target with the last element
        heap[index], heap[last_index] = heap[last_index], heap[index]
        heap.pop() # Remove the target

        # Check if we need to Bubble Up or Bubble Down
        parent = (index - 1) // 2
        if index > 0 and heap[index] > heap[parent]:
            bubble_up(heap, index)
        else:
            bubble_down(heap, index)
    else:
        heap.pop() # Target was already the last element

Complexity Analysis

Operation Time Complexity Explanation
Find Max (Peek) $O(1)$ The maximum is always instantly accessible at index 0.
Insert $O(\log n)$ In the worst case, the new value bubbles up from the very bottom to the root, which equals the height of the tree.
Extract Max $O(\log n)$ The swapped bottom element must bubble down to the bottom, taking time proportional to the tree's height.
Search / Arbitrary Delete $O(n)$ Because an array-based heap is only partially ordered (parents > children, but no left/right rules), finding a specific value requires scanning the entire array in $O(n)$ time. The deletion itself takes $O(\log n)$, but the search dominates the complexity.