The colors (indeed, using any color at all -- we could call them 0 and 1 trees!) are arbitrary. One story from one of the creators is that they had red and black pens handy!
T.nil: This is simply a space saving device: every "leaf" node of an ordinary BST gets left and right child pointers to a special node, T.nil, and the root node of the tree has its parent point to this as well.
And so, a simple way to get an intuition as
to why no leaf is further than
twice as far from the root as the nearest leaf: The
nearest leaf at the least bh levels from the root.
(The black-height of every node is the same.)
Since there is never more than one R-node
between any two B-nodes, at
most, the furthest node can be 2B levels away from
the root.
So we get:
A red-black tree with n internal nodes has height at
most 2 lg(n + 1).
Remember: This is a binary search tree.
So, non-modifying operations such as minimum(),
maximum(), successor(), predecessor(), and search() run
in O(height) time, and so for red-black trees, in O(lg
n) time.
But what about insert and delete? They are obviously
trickier. In fact, they are the whole trick: the
red-black properties are just a way of keeping the tree
roughly balanced.
But to get to insert and delete, we need to pass through rotations on the way.
1. d; 2. a; 3. a; 4. d;
Let's enter 6, 4, 2 in the:
Red-black tree visualizer
Note: In-order walks of the pre- and post-rotate trees produce the same output. It is still a BST!
Let's enter 2, 4, 6 in the:
Red-black tree visualizer
Here's the pseudo-code:
Left-Rotate(T, x)
y = x.right
x.right = y.left
if y.left != T.nil
y.left.p = x
y.p = x.p
if x.p == T.nil
T.root = y
elif x == x.p.left
x.p.left = y
else
x.p.right = y
y.left = x
x.p = y
Here's the pseudo-code:
RB-Insert(T, z)
y = T.nil
x = T.root
while x != T.nil
y = x
if z.key < x.key
x = x.left
else
x = x.right
z.p = y
if y == T.nil
T.root = z
elseif z.key < y.key
y.left = z
else
y.right = z
z.left = T.nil
z.right = T.nil
z.color = RED
RB-Insert-Fixup(T, z)
RB-Insert-Fixup(T, z)
while z.p.color == RED
if z.p == z.p.p.left
y = z.p.p.right
if y.color == RED
z.p.color = BLACK
y.color = BLACK
z.p.p.color = RED
z = z.p.p
else
if z == z.p.right
z = z.p
Left-Rotate(T, z)
z.p.color = BLACK
z.p.p.color = RED
Right-Rotate(T, z.p.p)
else // same as if with right and left reversed
...
T.root.color = BLACK
Each loop iteration we do one of two things: either we move up the tree,
The insert code itself (without fixup)
takes O(lg n) time, just like for a BST.
In fixup, the rotations occur in constant time.
We move up the tree while fixing, and this happens at most
lg n times.
Therefore, insert is O(lg n).
1. a; 2. c;
RB-Delete is going to rely on RB-Transplant:
RB-Transplant(t, u, v)
if u.p == T.nil
T.root = v
elsif u == u.p.left
u.p.left = v
else
u.p.right = v
v.p = u.p
And this will only be called on nodes for which it will
work: we don't call it on a node with two children asking
that one of them be its replacement without finding a
min or max, for instance.
So here is RB-Delete:
RB-Delete(T, x)
And here is RB-Delete-Fixup:
RB-Delete-Fixup(T, x)