Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Data Structures / Heaps and Priority Queues

Maintains a priority queue, returning the minimum element by default. The comparator comp defines priority: comp(a, b) is true when a has higher priority than b, so std::greater<T> instead creates a max-priority queue. A binary heap stores a heap-ordered binary tree implicitly in an array, where the children of the node at index $i$ live at indices $2i + 1$ and $2i + 2$. Insertion appends the new element and bubbles it up toward the root, while extraction moves the last element to the root and sifts it down. To customize the ordering, instantiate BinaryHeap<T, Compare> and pass the comparator to either constructor.

  • BinaryHeap<T>() constructs an empty priority queue.
  • BinaryHeap<T>(lo, hi) constructs a priority queue from the elements in the half-open iterator range $[{\htmlClass{math-inline-code}{\texttt{lo}}}, {\htmlClass{math-inline-code}{\texttt{hi}}})$.
  • size() returns the size of the priority queue.
  • empty() returns whether the priority queue is empty.
  • push(v) inserts the value v into the priority queue.
  • pop() removes the highest-priority element from the priority queue.
  • top() returns the highest-priority element in the priority queue.

Implementation

#include <cassert>
#include <functional>
#include <utility>
#include <vector>

template<typename T, typename Compare = std::less<T>>
class BinaryHeap {
  std::vector<T> heap;
  Compare comp;

  void sift_down(int i) {
    while (true) {
      int child = i * 2 + 1;
      if (child >= static_cast<int>(heap.size())) {
        break;
      }
      if (child + 1 < static_cast<int>(heap.size()) && comp(heap[child + 1], heap[child])) {
        child++;
      }
      if (comp(heap[child], heap[i])) {
        std::swap(heap[i], heap[child]);
        i = child;
      } else {
        break;
      }
    }
  }

 public:
  explicit BinaryHeap(Compare comp = Compare{}) : comp(std::move(comp)) {}

  template<typename It>
  BinaryHeap(It lo, It hi, Compare comp = Compare{}) : heap(lo, hi), comp(std::move(comp)) {
    for (int i = static_cast<int>(heap.size()) / 2 - 1; i >= 0; i--) {
      sift_down(i);
    }
  }

  int size() const { return static_cast<int>(heap.size()); }
  bool empty() const { return heap.empty(); }

  void push(const T &v) {
    heap.push_back(v);
    int i = static_cast<int>(heap.size()) - 1;
    while (i > 0) {
      int parent = (i - 1) / 2;
      if (!comp(heap[i], heap[parent])) {
        break;
      }
      std::swap(heap[i], heap[parent]);
      i = parent;
    }
  }

  void pop() {
    assert(!heap.empty());
    heap[0] = heap.back();
    heap.pop_back();
    if (!heap.empty()) {
      sift_down(0);
    }
  }

  const T &top() const {
    assert(!heap.empty());
    return heap[0];
  }
};

Example Usage

using namespace std;

int main() {
  vector<int> a{0, 5, -1, 12};
  BinaryHeap<int> h(a.begin(), a.end());
  assert(h.size() == 4);
  assert(h.top() == -1);
  h.push(10);
  vector<int> popped;
  while (!h.empty()) {
    popped.push_back(h.top());
    h.pop();
  }
  assert((popped == vector<int>{-1, 0, 5, 10, 12}));
  assert(h.empty());

  BinaryHeap<int, greater<int>> max_heap;
  max_heap.push(1);
  max_heap.push(3);
  assert(max_heap.top() == 3);
  return 0;
}