Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Data Structures / Dictionaries and Ordered Sets

Maintain an ordered map, that is, an ordered collection of key-value pairs such that each possible key appears at most once in the collection. A red-black tree is a binary search tree balanced by coloring its nodes red or black, then constraining node colors on any simple path from the root to a leaf. Specifically, a red node may never have a red child, and every path from the root to a null leaf must pass through the same number of black nodes, which together bound the tree's height to $O(\log n)$. Insertions and deletions repair these invariants by recoloring nodes and performing rotations.

The comparator comp defines the key ordering: comp(a, b) is true when a precedes b. The comparator defaults to std::less<K>; to customize the ordering, instantiate RedBlackTree<K, V, Compare> and pass the comparator to the constructor. The sentinel node also requires K and V to be default constructible.

  • RedBlackTree<K, V>() constructs an empty map.
  • size() returns the size of the map.
  • empty() returns whether the map is empty.
  • insert(k, v) adds an entry with key k and value v to the map, returning true if a new entry was added or false if the key already exists (in which case the map is unchanged and the old value associated with the key is preserved).
  • erase(k) removes the entry with key k from the map, returning true if the removal was successful or false if the key to be removed was not found.
  • find(k) returns a pointer to a const value associated with key k, or nullptr if the key was not found.
  • entries() returns all key-value entries in comparator order.

The comparator-aware navigation routines min(), max(), lower_bound(k), upper_bound(k), prev(k), and next(k) from the treap in 2.3.1 depend only on the BST property and may be adapted here as needed.

Implementation

#include <algorithm>
#include <functional>
#include <utility>
#include <vector>

template<typename K, typename V, typename Compare = std::less<K>>
class RedBlackTree {
  enum Color { RED, BLACK };
  struct Node {
    K key;
    V value;
    Color color;
    Node *left, *right, *parent;

    Node(const K &k, const V &v, Color c)
        : key(k), value(v), color(c), left(nullptr), right(nullptr), parent(nullptr) {}
  } *root, *LEAF_NIL;

  int num_nodes;
  Compare comp;

  void rotate_left(Node *n) {
    Node *tmp = n->right;
    if ((n->right = tmp->left) != LEAF_NIL) {
      n->right->parent = n;
    }
    if ((tmp->parent = n->parent) == LEAF_NIL) {
      root = tmp;
    } else if (n->parent->left == n) {
      n->parent->left = tmp;
    } else {
      n->parent->right = tmp;
    }
    tmp->left = n;
    n->parent = tmp;
  }

  void rotate_right(Node *n) {
    Node *tmp = n->left;
    if ((n->left = tmp->right) != LEAF_NIL) {
      n->left->parent = n;
    }
    if ((tmp->parent = n->parent) == LEAF_NIL) {
      root = tmp;
    } else if (n->parent->right == n) {
      n->parent->right = tmp;
    } else {
      n->parent->left = tmp;
    }
    tmp->right = n;
    n->parent = tmp;
  }

  void insert_fix(Node *n) {
    while (n->parent->color == RED) {
      Node *parent = n->parent;
      Node *grandparent = n->parent->parent;
      if (parent == grandparent->left) {
        Node *uncle = grandparent->right;
        if (uncle->color == RED) {
          grandparent->color = RED;
          parent->color = BLACK;
          uncle->color = BLACK;
          n = grandparent;
        } else {
          if (n == parent->right) {
            rotate_left(parent);
            n = parent;
            parent = n->parent;
          }
          rotate_right(grandparent);
          std::swap(parent->color, grandparent->color);
          break;
        }
      } else if (parent == grandparent->right) {
        Node *uncle = grandparent->left;
        if (uncle->color == RED) {
          grandparent->color = RED;
          parent->color = BLACK;
          uncle->color = BLACK;
          n = grandparent;
        } else {
          if (n == parent->left) {
            rotate_right(parent);
            n = parent;
            parent = n->parent;
          }
          rotate_left(grandparent);
          std::swap(parent->color, grandparent->color);
          break;
        }
      }
    }
    root->color = BLACK;
  }

  void replace(Node *n, Node *replacement) {
    if (n->parent == LEAF_NIL) {
      root = replacement;
    } else if (n == n->parent->left) {
      n->parent->left = replacement;
    } else {
      n->parent->right = replacement;
    }
    replacement->parent = n->parent;
  }

  void erase_fix(Node *n) {
    while (n != root && n->color == BLACK) {
      Node *parent = n->parent;
      if (n == parent->left) {
        Node *sibling = parent->right;
        if (sibling->color == RED) {
          sibling->color = BLACK;
          parent->color = RED;
          rotate_left(parent);
          sibling = parent->right;
        }
        if (sibling->left->color == BLACK && sibling->right->color == BLACK) {
          sibling->color = RED;
          n = parent;
        } else {
          if (sibling->right->color == BLACK) {
            sibling->left->color = BLACK;
            sibling->color = RED;
            rotate_right(sibling);
            sibling = parent->right;
          }
          sibling->color = parent->color;
          parent->color = BLACK;
          sibling->right->color = BLACK;
          rotate_left(parent);
          n = root;
        }
      } else {
        Node *sibling = parent->left;
        if (sibling->color == RED) {
          sibling->color = BLACK;
          parent->color = RED;
          rotate_right(parent);
          sibling = parent->left;
        }
        if (sibling->left->color == BLACK && sibling->right->color == BLACK) {
          sibling->color = RED;
          n = parent;
        } else {
          if (sibling->left->color == BLACK) {
            sibling->right->color = BLACK;
            sibling->color = RED;
            rotate_left(sibling);
            sibling = parent->left;
          }
          sibling->color = parent->color;
          parent->color = BLACK;
          sibling->left->color = BLACK;
          rotate_right(parent);
          n = root;
        }
      }
    }
    n->color = BLACK;
  }

  void collect_entries(Node *n, std::vector<std::pair<K, V>> &res) const {
    if (n != LEAF_NIL) {
      collect_entries(n->left, res);
      res.push_back({n->key, n->value});
      collect_entries(n->right, res);
    }
  }

  void clean_up(Node *n) {
    if (n != LEAF_NIL) {
      clean_up(n->left);
      clean_up(n->right);
      delete n;
    }
  }

 public:
  explicit RedBlackTree(Compare comp = Compare{}) : num_nodes(0), comp(std::move(comp)) {
    root = LEAF_NIL = new Node(K{}, V{}, BLACK);
    LEAF_NIL->left = LEAF_NIL->right = LEAF_NIL;
  }

  ~RedBlackTree() {
    clean_up(root);
    delete LEAF_NIL;
  }

  RedBlackTree(const RedBlackTree &) = delete;
  RedBlackTree &operator=(const RedBlackTree &) = delete;
  int size() const { return num_nodes; }
  bool empty() const { return num_nodes == 0; }

  bool insert(const K &k, const V &v) {
    Node *curr = root, *prev = LEAF_NIL;
    while (curr != LEAF_NIL) {
      prev = curr;
      if (comp(k, curr->key)) {
        curr = curr->left;
      } else if (comp(curr->key, k)) {
        curr = curr->right;
      } else {
        return false;
      }
    }
    Node *n = new Node(k, v, RED);
    n->parent = prev;
    if (prev == LEAF_NIL) {
      root = n;
    } else if (comp(k, prev->key)) {
      prev->left = n;
    } else {
      prev->right = n;
    }
    n->left = n->right = LEAF_NIL;
    insert_fix(n);
    num_nodes++;
    return true;
  }

  bool erase(const K &k) {
    Node *n = root;
    while (n != LEAF_NIL) {
      if (comp(k, n->key)) {
        n = n->left;
      } else if (comp(n->key, k)) {
        n = n->right;
      } else {
        break;
      }
    }
    if (n == LEAF_NIL) {
      return false;
    }
    Color color = n->color;
    Node *replacement;
    if (n->left == LEAF_NIL) {
      replacement = n->right;
      replace(n, n->right);
    } else if (n->right == LEAF_NIL) {
      replacement = n->left;
      replace(n, n->left);
    } else {
      Node *tmp = n->right;
      while (tmp->left != LEAF_NIL) {
        tmp = tmp->left;
      }
      color = tmp->color;
      replacement = tmp->right;
      if (tmp->parent == n) {
        replacement->parent = tmp;
      } else {
        replace(tmp, tmp->right);
        tmp->right = n->right;
        tmp->right->parent = tmp;
      }
      replace(n, tmp);
      tmp->left = n->left;
      tmp->left->parent = tmp;
      tmp->color = n->color;
    }
    delete n;
    if (color == BLACK) {
      erase_fix(replacement);
    }
    num_nodes--;
    return true;
  }

  const V *find(const K &k) const {
    Node *n = root;
    while (n != LEAF_NIL) {
      if (comp(k, n->key)) {
        n = n->left;
      } else if (comp(n->key, k)) {
        n = n->right;
      } else {
        return &(n->value);
      }
    }
    return nullptr;
  }

  std::vector<std::pair<K, V>> entries() const {
    std::vector<std::pair<K, V>> res;
    res.reserve(num_nodes);
    collect_entries(root, res);
    return res;
  }
};

Example Usage

#include <cassert>
using namespace std;

int main() {
  RedBlackTree<int, char> t;
  assert(t.empty());
  t.insert(2, 'b');
  t.insert(1, 'a');
  t.insert(3, 'c');
  t.insert(5, 'e');
  assert(t.insert(4, 'd'));
  assert(!t.empty() && t.size() == 5);
  assert(*t.find(4) == 'd');
  assert(!t.insert(4, 'd'));
  assert(t.size() == 5);
  assert(
      (t.entries() == vector<pair<int, char>>{{1, 'a'}, {2, 'b'}, {3, 'c'}, {4, 'd'}, {5, 'e'}})
  );
  assert(t.erase(1));
  assert(!t.erase(1));
  assert(t.find(1) == nullptr);
  assert(t.size() == 4);
  assert((t.entries() == vector<pair<int, char>>{{2, 'b'}, {3, 'c'}, {4, 'd'}, {5, 'e'}}));
  for (int key : {2, 3, 4, 5}) {
    assert(t.erase(key));
  }
  assert(t.empty() && t.size() == 0);

  RedBlackTree<int, char, greater<int>> descending;
  for (int key : {2, 1, 3}) {
    descending.insert(key, '0' + key);
  }
  assert((descending.entries() == vector<pair<int, char>>{{3, '3'}, {2, '2'}, {1, '1'}}));
  assert(*descending.find(2) == '2');
  assert(descending.erase(2) && descending.find(2) == nullptr);
  return 0;
}