/// <summary> /// Perform rebalancing on the red-black tree for a deletion. O(lg n). /// If we broke the tree by moving a black node, we need to reorder /// and/or recolor part of it. This is not pretty, and is basically /// derived from the algorithm in Cormen/Leiserson/Rivest, just like /// every other red-black tree implementation. We have, however, gone /// to the necessary work to drop out the required sentinel value in /// their version, so this is a little more flexible. /// </summary> /// <param name="child">The child that was removed.</param> /// <param name="parent">The parent it was removed from.</param> /// <param name="rightSide">Which side of the parent it was removed from.</param> private void DetachFixup(WeightedRedBlackTreeNode <K, V> child, WeightedRedBlackTreeNode <K, V> parent, bool rightSide) { while (child != Root && child.IsBlack()) { if (!rightSide) { WeightedRedBlackTreeNode <K, V> other = parent.Right; if (other.IsRed()) { other.MakeNodeBlack(); parent.MakeNodeRed(); RotateLeft(parent); other = parent.Right; } if (other == null || (other.Left.IsBlack() && other.Right.IsBlack())) { other.MakeNodeRed(); child = parent; } else { if (other.Right.IsBlack()) { other.Left.MakeNodeBlack(); other.MakeNodeRed(); RotateRight(other); other = parent.Right; } if (parent.IsRed()) { other.MakeNodeRed(); } else { other.MakeNodeBlack(); } parent.MakeNodeBlack(); other.Right.MakeNodeBlack(); RotateLeft(parent); child = Root; } } else { WeightedRedBlackTreeNode <K, V> other = parent.Left; if (other.IsRed()) { other.MakeNodeBlack(); parent.MakeNodeRed(); RotateRight(parent); other = parent.Left; } if (other == null || (other.Right.IsBlack() && other.Left.IsBlack())) { other.MakeNodeRed(); child = parent; } else { if (other.Left.IsBlack()) { other.Right.MakeNodeBlack(); other.MakeNodeRed(); RotateLeft(other); other = parent.Left; } if (parent.IsRed()) { other.MakeNodeRed(); } else { other.MakeNodeBlack(); } parent.MakeNodeBlack(); other.Left.MakeNodeBlack(); RotateRight(parent); child = Root; } } parent = child.Parent; if (child != Root) { rightSide = (child == parent.Right); } } child.MakeNodeBlack(); }
/// <summary> /// Validate the correctness of the red-black tree according to the four /// red-black properties [1]: /// /// 1. Every node is either red or black. /// 2. Every leaf (null) is black. /// 3. If a node is red, then both its children are black. /// 4. Every simple path from a node to a descendant leaf contains the /// same number of black nodes. /// /// In addition, we also test that: /// /// 5. Only the Root node may have a null Parent pointer. /// 6. For all non-null Left and Right pointers, Left.Parent == parent, /// and Right.Parent == parent. /// 7. The weight of each node equals the weight of its Left child plus /// the weight of its Right child plus one. /// /// The first three are trivial to test. Properties 5 and 6 are also /// easy, and ensure that the tree is a valid binary search tree. Property /// 4 is the only one that's interesting; to test it, we recursively walk /// the tree inorder after everything else has been checked, and compare /// the count of black nodes each time we reach 'null' against the count of /// black nodes from a strict walk of Left pointers. /// </summary> /// <throws cref="InvalidOperationException">Thrown if the tree is invalid /// or damaged.</throws> public override void Validate() { void Assert(bool result) { if (!result) { throw new InvalidOperationException("Tree is invalid."); } } // Test #2. Every leaf (null) is black. Assert(((WeightedRedBlackTreeNode <K, V>)null).IsBlack()); Assert(!((WeightedRedBlackTreeNode <K, V>)null).IsRed()); // Count up the black nodes in a simple path from root to leaf (null). int expectedBlackCount = 0; for (WeightedRedBlackTreeNode <K, V> node = Root; node != null; node = node.Left) { if (node.IsBlack()) { expectedBlackCount++; } } expectedBlackCount++; // Count up one more for the null. // Recursively walk the entire tree, validating each node. void RecursivelyValidate(WeightedRedBlackTreeNode <K, V> node, int currentBlackCount) { // Count up an additional black node in this path, if this node is black. if (node.IsBlack()) { currentBlackCount++; } // Test #1. Every node is either red or black. Assert(node.IsRed() || node.IsBlack()); // Test #3. If a node is red, then both its children are black. if (node.IsRed()) { Assert(node.Left.IsBlack()); Assert(node.Right.IsBlack()); } // Test #5. Only the Root node may have a null Parent pointer. if (node != null && node.Parent == null) { Assert(node == Root); } // Test #6. For all non-null Left and Right pointers, // Left.Parent == parent, and Right.Parent == parent. if (node != null && node.Left != null) { Assert(node.Left.Parent == node); } if (node != null && node.Right != null) { Assert(node.Right.Parent == node); } // Test #4. Every simple path from a node to a descendant leaf // contains the same number of black nodes. if (currentBlackCount == expectedBlackCount) { Assert(node == null); } if (node != null) { // Test #7. The weight of each node equals the weight of its Left child plus // the weight of its Right child plus one. int expectedWeight = (node.Left?.Weight ?? 0) + (node.Right?.Weight ?? 0) + 1; Assert(node.Weight == expectedWeight); } // Recurse to test the rest of the tree. if (node != null) { RecursivelyValidate(node.Left, currentBlackCount); RecursivelyValidate(node.Right, currentBlackCount); } } // Recursively check the entire tree, starting at the root. RecursivelyValidate(Root, 0); }
/// <summary> /// This corrects the tree for a newly-inserted node, rotating it as /// necessary to keep it balanced. The algorithm is the same as RB-INSERT in [1], /// minus the first line that performs TREE-INSERT. O(lg n). /// </summary> /// <param name="node">The newly-inserted node.</param> protected void InsertFixup(WeightedRedBlackTreeNode <K, V> node) { node.MakeNodeRed(); WeightedRedBlackTreeNode <K, V> parent = node.Parent; while (node != Root && parent.IsRed()) { if (parent == parent.Parent.Left) { WeightedRedBlackTreeNode <K, V> uncle = parent.Parent.Right; if (uncle.IsRed()) { parent.MakeNodeBlack(); uncle.MakeNodeBlack(); parent.Parent.MakeNodeRed(); node = parent.Parent; parent = node.Parent; } else { if (node == parent.Right) { node = parent; RotateLeft(node); parent = node.Parent; } parent.MakeNodeBlack(); parent.Parent.MakeNodeRed(); RotateRight(parent.Parent); parent = node.Parent; } } else { WeightedRedBlackTreeNode <K, V> uncle = parent.Parent.Left; if (uncle.IsRed()) { parent.MakeNodeBlack(); uncle.MakeNodeBlack(); parent.Parent.MakeNodeRed(); node = parent.Parent; parent = node.Parent; } else { if (node == parent.Left) { node = parent; RotateRight(node); parent = node.Parent; } parent.MakeNodeBlack(); parent.Parent.MakeNodeRed(); RotateLeft(parent.Parent); parent = node.Parent; } } } Root.MakeNodeBlack(); }