Augmented Red-Black Tree
In the previous post, I used an augmented binary search tree to solve the following problem in \(O(n \log n)\) average time:
You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].
In this post, I’ll modify the algorithm to work for a self-balancing binary search tree. A self-balancing BST is a BST that always keeps its height small - of the order of \(O(\log n)\). Insertions into a balanced BST always take \(O(\log n)\) time, and thus my algorithm will have \(O(n \log n)\) worst-case complexity. One kind of self-balancing BSTs are red-black trees.
A red-black tree is a binary search tree where each node stores stores an additional color attribute, which satisfies the following properties:
- Each node can be either red or black.
- The root is black.
- All leaves are black and have NIL value.
- All simple paths from the root to leaves have the same number of black nodes.
- No two red nodes are adjacent.
Thanks to properties 4 and 5, a red-black tree with \(n\) nodes has height at most \(2(\lg n + 1)\), so insertion into a red-black tree has a worst-case \(O (\log n)\) complexity.
After a new node is inserted, the red-black tree goes through a fixup procedure to ensure that the properties 1-5 are fulfilled. I am not going to describe it in detail; you can read about it in Wikipedia or examine the code below. The important thing about the fixup procedure is that it only uses two basic operations: recoloring nodes and rotation.
The rotation operations on a binary search tree works as follows:
The operation LeftRotate transforms the configuration of the two nodes on the right into the configuration on the left. The inverse operation RightRotate transforms the configuration on the left into the configuration on the right. The letters \(\alpha\), \(\beta\) and \(\gamma\) represent arbitrary subtrees.
In the previous post, we augmented a node with an “lc” field to store the number of nodes in the lest subtree:
To augment a red-black tree the same way, we need to ensure that this field is correctly updated during the fixup operation. Recoloring doesn’t change this number, and during rotation we only need to update the y node.
For the right rotation,
For the left rotation,
The full algorithm is below:
After moving a little further in studing data structures, I realized my invention closely resembles an Order Statistic Tree
An Order Statistic tree is a self-balancing binary tree where each node stores the total number of nodes below it, including itself (the NIL nodes’ size is defined as zero). An Order Statistic tree allows to
- insert elements in \(O(\log n)\) time
- find the rank of an element in \(O(\log n)\) time
- find the kth smallest element in \(O(\log n)\) time
So, an order statistic tree would serve just as well for solving our problem.
In the next post, I’ll describe another way to efficiently solve this problem with a different data structure: Fenwick tree.