<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. DSA
  3. Trees
All lessons in this course

Lesson 29 of 50 15 minIntermediate

Heaps and priority queues

Store a complete binary tree in an array, keep the smallest item on top with sift-up and sift-down in O(log n), and use .NET’s PriorityQueue for scheduling, top-k and merging sorted data.

A queue serves items in arrival order. Often you need something else: the most urgent item first. The next site inspection by priority, the closest unvisited node in a route search, the job with the earliest deadline. A priority queue supports exactly three things: add an item, look at the best item, remove the best item. The standard way to build one is a binary heap.

The heap rule

A min-heap is a binary tree where every parent is ≤ its children. So the smallest item is always at the root. Unlike a BST, there is no left/right order: siblings can be in any order. That weaker rule is what makes it cheap to maintain.

A heap is also always a complete tree: every level is full except possibly the last, which fills from the left. A complete tree fits perfectly into an array with no gaps and no pointers:

For the item at index iIndex
Parent(i − 1) / 2
Left child2i + 1
Right child2i + 2
Interactive

A min-heap: tree view and array view

Every parent is ≤ its children, so the smallest item is always at the root, index 0. The children of index i are at 2i + 1 and 2i + 2.

308152123947566
Array
031825312495766
A min-heap of 7 items. Push a value or pop the minimum, then press Next step to watch it sift.

Push: add at the end, sift up

Put the new item in the next free slot (the end of the array), so the tree stays complete. It may be smaller than its parent, so swap it upwards while it is. At most one swap per level: O(log n).

Pop: take the root, sift down

The minimum is at index 0. Move the last item into its place (again keeping the tree complete), then swap it down with its smaller child while it is bigger than that child. Also O(log n). Looking at the minimum without removing it (Peek) is O(1).

MinHeap.cs
var heap = new MinHeap();foreach (int x in new[] { 9, 4, 7, 1, 8, 2 }) heap.Push(x); var order = new List<int>();while (heap.Count > 0) order.Add(heap.Pop());Console.WriteLine(string.Join(" ", order)); class MinHeap{    private readonly List<int> _a = new();    public int Count => _a.Count;    public int Peek() => _a[0];     public void Push(int x)    {        _a.Add(x);                                      // add at the end…        int i = _a.Count - 1;        while (i > 0)        {            int parent = (i - 1) / 2;            if (_a[parent] <= _a[i]) break;             // …sift up while smaller than the parent            (_a[parent], _a[i]) = (_a[i], _a[parent]);            i = parent;        }    }     public int Pop()    {        int min = _a[0];        _a[0] = _a[^1];                                 // move the last item to the root…        _a.RemoveAt(_a.Count - 1);        int i = 0;        while (true)                                    // …and sift it down        {            int l = 2 * i + 1, r = l + 1, smallest = i;            if (l < _a.Count && _a[l] < _a[smallest]) smallest = l;            if (r < _a.Count && _a[r] < _a[smallest]) smallest = r;            if (smallest == i) break;            (_a[smallest], _a[i]) = (_a[i], _a[smallest]);            i = smallest;        }        return min;    }}
Output
1 2 4 7 8 9

Pushing everything and then popping everything sorts the data: that is heap sort, O(n log n) and in place when done inside the array itself. Building a heap from n items at once (“heapify”, sifting down from the middle of the array backwards) is only O(n).

PriorityQueue in .NET

.NET 6 added PriorityQueue<TElement, TPriority>, a min-heap where each element has a separate priority. The smallest priority comes out first.

PriorityQueue.cs
var jobs = new PriorityQueue<string, int>();jobs.Enqueue("Check crane certificate", 2);jobs.Enqueue("Fix missing safety rail", 1);jobs.Enqueue("Order extra bolts", 4);jobs.Enqueue("Inspect welds on level 3", 3); Console.WriteLine($"Next: {jobs.Peek()}");while (jobs.TryDequeue(out var job, out int priority))    Console.WriteLine($"{priority}: {job}");
Output
Next: Fix missing safety rail
1: Fix missing safety rail
2: Check crane certificate
3: Inspect welds on level 3
4: Order extra bolts

Classic use: the k largest

To find the 3 longest beams out of a million, sorting everything costs O(n log n). Instead keep a min-heap of size k: push each item, and when the heap holds more than k, pop the smallest. The heap always holds the k largest seen so far. Cost: O(n log k), and only k items of memory.

TopK.cs
static List<int> TopK(IEnumerable<int> values, int k){    var heap = new PriorityQueue<int, int>();   // min-heap of the k largest so far    foreach (int v in values)    {        heap.Enqueue(v, v);        if (heap.Count > k) heap.Dequeue();     // drop the smallest    }    var result = new List<int>();    while (heap.Count > 0) result.Add(heap.Dequeue());    result.Reverse();                           // largest first    return result;} Console.WriteLine(string.Join(" ", TopK(new[] { 5, 12, 3, 41, 7, 19, 8, 26 }, 3)));
Output
41 26 19
OperationBinary heapSorted listUnsorted list
AddO(log n)O(n)O(1)
Peek bestO(1)O(1)O(n)
Remove bestO(log n)O(1) from the endO(n)
Search for any itemO(n)O(log n)O(n)
Quick check

In a heap stored in an array, where are the children of the item at index 4?

Quick check

Is a min-heap’s array sorted?

Practice

Merge k sorted arrays into one sorted list using a PriorityQueue. For { 1, 4, 9 }, { 2, 3, 10 }, { 5, 6 } the result is 1 2 3 4 5 6 9 10. With N items in total and k arrays, aim for O(N log k).

Show a hint

Put the first item of each array into the queue, with a note of which array and index it came from. Repeatedly dequeue the smallest, add it to the result, and enqueue the next item from the same array.

Show the solution
Solution.cs
var parts = new List<int[]> { new[] { 1, 4, 9 }, new[] { 2, 3, 10 }, new[] { 5, 6 } };Console.WriteLine(string.Join(" ", MergeK(parts))); static List<int> MergeK(List<int[]> lists){    var pq = new PriorityQueue<(int list, int index), int>();    for (int i = 0; i < lists.Count; i++)        if (lists[i].Length > 0) pq.Enqueue((i, 0), lists[i][0]);     var result = new List<int>();    while (pq.TryDequeue(out var pos, out int value))    {        result.Add(value);        int next = pos.index + 1;        if (next < lists[pos.list].Length)            pq.Enqueue((pos.list, next), lists[pos.list][next]);   // next item from the same array    }    return result;}
Output
1 2 3 4 5 6 9 10

Remember

  • A min-heap keeps every parent ≤ its children, so the minimum is at the root.
  • A heap is a complete tree stored in an array: parent (i − 1) / 2, children 2i + 1 and 2i + 2.
  • Push sifts up, pop sifts down: O(log n). Peek is O(1); heapify is O(n).
  • PriorityQueue<TElement, TPriority> is .NET’s min-heap; it is not stable. Use a size-k heap for top-k.
0 of 50 lessons completed