After playing around with the Red-Black Tree implementation example in the Clojure Cookbook I noticed that the balancing function does not contain the recoloring case. (Case 1 in most of the Red-Black Tree literature, or also known as the case where the uncle of the inserted node is red).
(defn balance
"Ensures the given subtree stays balanced by rearranging black nodes
that have at least one red child and one red grandchild"
[tree]
(match [tree]
[(:or ;; Left child red with left red grandchild
[:black [:red [:red a x b] y c] z d]
;; Left child red with right red grandchild
[:black [:red a x [:red b y c]] z d]
;; Right child red with left red grandchild
[:black a x [:red [:red b y c] z d]]
;; Right child red with right red grandchild
[:black a x [:red b y [:red c z d]]])] [:red [:black a x b]
y
[:black c z d]]
:else tree))
Here is a little example:
Inserting the number 8
in tree a
should in my opinion produce tree b
by recoloring the second level. The implementation in the Clojure Cookbook rotates the tree unneccessarily producing tree c
.
Is there a good reason to leave out this case in the implementation?
A red-black tree is a Binary tree where a particular node has color as an extra attribute, either red or black. By check the node colors on any simple path from the root to a leaf, red-black trees secure that no such path is higher than twice as long as any other so that the tree is generally balanced.
A red-black tree is a kind of self-balancing binary search tree where each node has an extra bit, and that bit is often interpreted as the color (red or black). These colors are used to ensure that the tree remains balanced during insertions and deletions.
C is not valid red-black tree as the black height from root to leaf is not the same.
Definition of a red-black tree A red-black tree is a binary search tree which has the following red-black properties: Every node is either red or black. Every leaf (NULL) is black. If a node is red, then both its children are black.
I'm the author of this particular recipe.
Most Red-Black tree implementations and text books assume a mutable context in which you can update specific nodes in-place. In such contexts, changing the color of a node is definitely cheaper than a tree rotation.
However, the Clojure Book implementation is purely functional, meaning there are no mutations. As such, whether you're recoloring the nodes or creating a new subtree, the cost is the same since we're copying the nodes anyway. Therefore we go with the rotation. This allows the balance function to be as simple as it is while maintaining all Red-Black properties.
This implementation is inspired by Chris Okasaki's book Purely Functional Data Structures. It's an excellent reference I would recommend to anyone interested in persistent data structures.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With