/*************************************************************************************************/ /// <summary> /// Insert data item to tree /// </summary> public override void Insert(TKey key, TValue value) { var newNode = new RedBlackTreeMapNode <TKey, TValue>(key, value); // Invoke the super BST insert node method. // This insert node recursively starting from the root and checks for success status (related to allowDuplicates flag). // The functions increments count on its own. var success = base._insertNode(newNode); if (success == false && _allowDuplicates == false) { throw new InvalidOperationException("Tree does not allow inserting duplicate elements."); } // Adjust Red-Black Tree rules if (!newNode.IsEqualTo(Root)) { if (newNode.Parent.Color != RedBlackTreeColors.Black) // Case 0: Parent is not black and we have to restructure tree { _adjustTreeAfterInsertion(newNode); } } // Always color root as black Root.Color = RedBlackTreeColors.Black; }
protected virtual RedBlackTreeColors _safeGetColor(RedBlackTreeMapNode <TKey, TValue> node) { if (node == null) { return(RedBlackTreeColors.Black); } return(node.Color); }
protected RedBlackTreeMapNode <TKey, TValue> _safeGetRightChild(RedBlackTreeMapNode <TKey, TValue> node) { if (node == null) { return(null); } return(node.RightChild); }
protected RedBlackTreeMapNode <TKey, TValue> _safeGetSibling(RedBlackTreeMapNode <TKey, TValue> node) { if (node == null || node.Parent == null) { return(null); } return(node.Sibling); }
/*************************************************************************************************/ /*** * Safety Checks/Getters/Setters. * * The following are helper methods for safely checking, getting and updating possibly-null objects. * These helpers make the algorithms of adjusting the tree after insertion and removal more readable. */ protected RedBlackTreeMapNode <TKey, TValue> _safeGetGrandParent(RedBlackTreeMapNode <TKey, TValue> node) { if (node == null || node.Parent == null) { return(null); } return(node.GrandParent); }
protected virtual void _safeUpdateColor(RedBlackTreeMapNode <TKey, TValue> node, RedBlackTreeColors color) { if (node == null) { return; } node.Color = color; }
/// <summary> /// Rotates a node to the right in the Red-Black Tree. /// </summary> protected virtual void _rotateRightAt(RedBlackTreeMapNode <TKey, TValue> currentNode) { // We check the right child because it's going to be a pivot node for the rotation if (currentNode == null || currentNode.HasLeftChild == false) { return; } // Pivot on *left* child var pivotNode = currentNode.LeftChild; // Parent of currentNode var parent = currentNode.Parent; // Check if currentNode is it's parent's left child. bool isLeftChild = currentNode.IsLeftChild; // Check if currentNode is the Root bool isRootNode = (currentNode == this.Root); // Perform the rotation currentNode.LeftChild = pivotNode.RightChild; pivotNode.RightChild = currentNode; // Update parents references currentNode.Parent = pivotNode; pivotNode.Parent = parent; if (currentNode.HasLeftChild) { currentNode.LeftChild.Parent = currentNode; } // Update the entire tree's Root if necessary if (isRootNode) { this.Root = pivotNode; } // Update the original parent's child node if (isLeftChild) { parent.LeftChild = pivotNode; } else if (parent != null) { parent.RightChild = pivotNode; } }
/// <summary> /// The internal remove helper. /// Separated from the overriden version to avoid casting the objects from BSTMapNode to RedBlackTreeMapNode. /// This is called from the overriden _remove(BSTMapNode nodeToDelete) helper. /// </summary> protected bool _remove(RedBlackTreeMapNode <TKey, TValue> nodeToDelete) { if (nodeToDelete == null) { return(false); } // Temporary nodes RedBlackTreeMapNode <TKey, TValue> node1, node2; // If nodeToDelete has either one child or no children at all if (!nodeToDelete.HasLeftChild || !nodeToDelete.HasRightChild) { node1 = nodeToDelete; } else { // nodeToDelete has two children node1 = (RedBlackTreeMapNode <TKey, TValue>)base._findNextLarger(nodeToDelete); } // Left child case if (node1.HasLeftChild) { node2 = node1.LeftChild; } else { node2 = node1.RightChild; } // If node2 is not null, copy parent references if (node2 != null) { node2.Parent = node1.Parent; } if (node1.Parent != null) { if (node1.IsLeftChild) { node1.Parent.LeftChild = node2; } else { node1.Parent.RightChild = node2; } } else { Root = node2; Root.Parent = null; } // Swap values if (!node1.IsEqualTo(nodeToDelete)) { nodeToDelete.Key = node1.Key; nodeToDelete.Value = node1.Value; } // Adjust the Red-Black Tree rules if (node1.Color == RedBlackTreeColors.Black && node2 != null) { _adjustTreeAfterRemoval(node2); Root.Color = RedBlackTreeColors.Black; } // Decrement the count base._count--; return(true); }
/// <summary> /// After removal tree-adjustement helper. /// </summary> protected virtual void _adjustTreeAfterRemoval(RedBlackTreeMapNode <TKey, TValue> currentNode) { while (currentNode != null && currentNode != Root && currentNode.IsBlack) { if (currentNode.IsLeftChild) { // Get sibling of currentNode // Safe equivalent of currentNode.Sibling or currentNode.Parent.RightChild var sibling = _safeGetRightChild(_safeGetParent(currentNode)); // Safely check sibling.IsRed property if (_safeCheckIsRed(sibling)) { // Color currentNode.Sibling as black _safeUpdateColor(sibling, RedBlackTreeColors.Black); // Color currentNode.Parent as red _safeUpdateColor(_safeGetParent(currentNode), RedBlackTreeColors.Red); // Left Rotate on currentNode's parent _rotateLeftAt(_safeGetParent(currentNode)); // Update sibling reference // Might end be being set to null sibling = _safeGetRightChild(_safeGetParent(currentNode)); } // Check if the left and right children of the sibling node are black // Use the safe methods to check for: (sibling.LeftChild.IsBlack && sibling.RightChild.IsBlack) if (_safeCheckIsBlack(_safeGetLeftChild(sibling)) && _safeCheckIsBlack(_safeGetRightChild(sibling))) { // Color currentNode.Sibling as red _safeUpdateColor(sibling, RedBlackTreeColors.Red); // Assign currentNode.Parent to currentNode currentNode = _safeGetParent(currentNode); } else { if (_safeCheckIsBlack(_safeGetRightChild(sibling))) { // Color currentNode.Sibling.LeftChild as black _safeUpdateColor(_safeGetLeftChild(sibling), RedBlackTreeColors.Black); // Color currentNode.Sibling as red _safeUpdateColor(sibling, RedBlackTreeColors.Red); // Right Rotate on sibling _rotateRightAt(sibling); // Update sibling reference // Might end be being set to null sibling = _safeGetRightChild(_safeGetParent(currentNode)); } // Color the Sibling node as currentNode.Parent.Color _safeUpdateColor(sibling, _safeGetColor(_safeGetParent(currentNode))); // Color currentNode.Parent as black _safeUpdateColor(_safeGetParent(currentNode), RedBlackTreeColors.Black); // Color Sibling.RightChild as black _safeUpdateColor(_safeGetRightChild(sibling), RedBlackTreeColors.Black); // Rotate on currentNode's parent _rotateLeftAt(_safeGetParent(currentNode)); currentNode = Root; } } else { // Get sibling of currentNode // Safe equivalent of currentNode.Sibling or currentNode.Parent.LeftChild var sibling = _safeGetLeftChild(_safeGetParent(currentNode)); if (_safeCheckIsRed(sibling)) { // Color currentNode.Sibling as black _safeUpdateColor(sibling, RedBlackTreeColors.Black); // Color currentNode.Parent as red _safeUpdateColor(_safeGetParent(currentNode), RedBlackTreeColors.Red); // Right Rotate tree around the parent of currentNode _rotateRightAt(_safeGetParent(currentNode)); // Update sibling reference // Might end be being set to null sibling = _safeGetLeftChild(_safeGetParent(currentNode)); } // Check if the left and right children of the sibling node are black // Use the safe methods to check for: (sibling.LeftChild.IsBlack && sibling.RightChild.IsBlack) if (_safeCheckIsBlack(_safeGetLeftChild(sibling)) && _safeCheckIsBlack(_safeGetRightChild(sibling))) { _safeUpdateColor(sibling, RedBlackTreeColors.Red); // Assign currentNode.Parent to currentNode currentNode = _safeGetParent(currentNode); } else { // Check if sibling.LeftChild.IsBlack == true if (_safeCheckIsBlack(_safeGetLeftChild(sibling))) { // Color currentNode.Sibling.RightChild as black _safeUpdateColor(_safeGetRightChild(sibling), RedBlackTreeColors.Black); // Color currentNode.Sibling as red _safeUpdateColor(sibling, RedBlackTreeColors.Red); // Left rotate on sibling _rotateLeftAt(sibling); // Update sibling reference // Might end be being set to null sibling = _safeGetLeftChild(_safeGetParent(currentNode)); } // Color the Sibling node as currentNode.Parent.Color _safeUpdateColor(sibling, _safeGetColor(_safeGetParent(currentNode))); // Color currentNode.Parent as black _safeUpdateColor(_safeGetParent(currentNode), RedBlackTreeColors.Black); // Color Sibling.RightChild as black _safeUpdateColor(_safeGetLeftChild(sibling), RedBlackTreeColors.Black); // Right rotate on the parent of currentNode _rotateRightAt(_safeGetParent(currentNode)); currentNode = Root; } } } // Color currentNode as black _safeUpdateColor(currentNode, RedBlackTreeColors.Black); }
/// <summary> /// After insertion tree-adjustement helper. /// </summary> protected virtual void _adjustTreeAfterInsertion(RedBlackTreeMapNode <TKey, TValue> currentNode) { // // STEP 1: // Color the currentNode as red _safeUpdateColor(currentNode, RedBlackTreeColors.Red); // // STEP 2: // Fix the double red-consecutive-nodes problems, if there exists any. if (currentNode != null && currentNode != Root && _safeCheckIsRed(_safeGetParent(currentNode))) { // // STEP 2.A: // This is the simplest step: Basically recolor, and bubble up to see if more work is needed. if (_safeCheckIsRed(_safeGetSibling(currentNode.Parent))) { // If it has a sibling and it is red, then then it has a parent currentNode.Parent.Color = RedBlackTreeColors.Red; // Color sibling of parent as black _safeUpdateColor(_safeGetSibling(currentNode.Parent), RedBlackTreeColors.Black); // Color grandparent as red _safeUpdateColor(_safeGetGrandParent(currentNode), RedBlackTreeColors.Red); // Adjust on the grandparent of currentNode _adjustTreeAfterInsertion(_safeGetGrandParent(currentNode)); } // // STEP 2.B: // Restructure the tree if the parent of currentNode is a left child to the grandparent of currentNode // (parent is a left child to its own parent). // If currentNode is also a left child, then do a single right rotation; otherwise, a left-right rotation. // // using the safe methods to check: currentNode.Parent.IsLeftChild == true else if (_safeGetParent(currentNode) == _safeGetLeftChild(_safeGetGrandParent(currentNode))) { if (currentNode.IsRightChild) { currentNode = _safeGetParent(currentNode); _rotateLeftAt(currentNode); } // Color parent as black _safeUpdateColor(_safeGetParent(currentNode), RedBlackTreeColors.Black); // Color grandparent as red _safeUpdateColor(_safeGetGrandParent(currentNode), RedBlackTreeColors.Red); // Right Rotate tree around the currentNode's grand parent _rotateRightAt(_safeGetGrandParent(currentNode)); } // // STEP 2.C: // Restructure the tree if the parent of currentNode is a right child to the grandparent of currentNode // (parent is a right child to its own parent). // If currentNode is a right-child in it's parent, then do a single left rotation; otherwise a right-left rotation. // // using the safe methods to check: currentNode.Parent.IsRightChild == true else if (_safeGetParent(currentNode) == _safeGetRightChild(_safeGetGrandParent(currentNode))) { if (currentNode.IsLeftChild) { currentNode = _safeGetParent(currentNode); _rotateRightAt(currentNode); } // Color parent as black _safeUpdateColor(_safeGetParent(currentNode), RedBlackTreeColors.Black); // Color grandparent as red _safeUpdateColor(_safeGetGrandParent(currentNode), RedBlackTreeColors.Red); // Left Rotate tree around the currentNode's grand parent _rotateLeftAt(_safeGetGrandParent(currentNode)); } } // STEP 3: // Color the root node as black _safeUpdateColor(Root, RedBlackTreeColors.Black); }
protected virtual bool _safeCheckIsRed(RedBlackTreeMapNode <TKey, TValue> node) { return(node != null && node.IsRed); }