/// <summary> /// Fixes this node by combining parent entry downwards. /// </summary> void FixByCombine(int targetParentIndex, TreeNode <K, V> leftChild, TreeNode <K, V> rightChild, TreeNode <K, V> parent) { // Move the entry at targetParentIndex from parent to left child. leftChild.entries.Add(parent.GetEntry(targetParentIndex)); // Move all elements in the right child to the left child. leftChild.entries.AddRange(rightChild.entries); leftChild.childrenIds.AddRange(rightChild.childrenIds); // Update parent of the children moved from right child. for (int i = 0; i < rightChild.childrenIds.Count; i++) { var childNode = nodeManager.Find(rightChild.childrenIds[i]); childNode.parentId = leftChild.id; nodeManager.MarkAsChanged(childNode); } // Remove the entry at targetParentIndex and rightChild node from parent. parent.entries.RemoveAt(targetParentIndex); parent.childrenIds.RemoveAt(targetParentIndex + 1); // Node no longer used. nodeManager.Delete(rightChild); // If the parent node is a root and no longer has an entry if (parent.parentId == 0 && parent.EntryCount == 0) { // Make the left child root leftChild.parentId = 0; nodeManager.MarkAsChanged(leftChild); nodeManager.MakeRoot(leftChild); nodeManager.Delete(parent); } // If the parent node is not a root and doesn't have the minimum entry count else if (parent.parentId != 0 && parent.EntryCount < nodeManager.MinEntriesPerNode) { nodeManager.MarkAsChanged(leftChild); nodeManager.MarkAsChanged(parent); // Fix the parent node. parent.FixNode(); } // Else, just finalize. else { nodeManager.MarkAsChanged(leftChild); nodeManager.MarkAsChanged(parent); } }
// // Private Methods // /// <summary> /// Rebalance this node after an element has been removed causing it to underflow /// </summary> void Rebalance() { // If the deficient node's right sibling exists and has more // than the minimum number of elements, then rotate left var indexInParent = IndexInParent(); var parent = nodeManager.Find(parentId); var rightSibling = ((indexInParent + 1) < parent.ChildrenNodeCount) ? parent.GetChildNode(indexInParent + 1) : null; if ((rightSibling != null) && (rightSibling.EntriesCount > nodeManager.MinEntriesPerNode)) { // Copy the separator from the parent to the end of the deficient node // (the separator moves down; the deficient node now has the minimum number of elements) entries.Add(parent.GetEntry(indexInParent)); // Replace the separator in the parent with the first element of the right sibling // (right sibling loses one node but still has at least the minimum number of elements) parent.entries[indexInParent] = rightSibling.entries[0]; rightSibling.entries.RemoveAt(0); // Move the first child reference from right sibling to me. if (false == rightSibling.IsLeaf) { // First, update parentId of the child that will be moved var n = nodeManager.Find(rightSibling.childrenIds[0]); n.parentId = this.id; nodeManager.MarkAsChanged(n); // Then move it childrenIds.Add(rightSibling.childrenIds[0]); rightSibling.childrenIds.RemoveAt(0); } // The tree is now balanced nodeManager.MarkAsChanged(this); nodeManager.MarkAsChanged(parent); nodeManager.MarkAsChanged(rightSibling); return; } // Otherwise, if the deficient node's left sibling exists and has more // than the minimum number of elements, then rotate right var leftSibling = ((indexInParent - 1) >= 0) ? parent.GetChildNode(indexInParent - 1) : null; if ((leftSibling != null) && (leftSibling.EntriesCount > nodeManager.MinEntriesPerNode)) { // Copy the separator from the parent to the start of the deficient node // (the separator moves down; deficient node now has the minimum number of elements) entries.Insert(0, parent.GetEntry(indexInParent - 1)); // Replace the separator in the parent with the last element // of the left sibling (left sibling loses one node but still has // at least the minimum number of elements) parent.entries[indexInParent - 1] = leftSibling.entries[leftSibling.entries.Count - 1]; leftSibling.entries.RemoveAt(leftSibling.entries.Count - 1); // Move the last child reference from the left sibing to me. // First, update parentId of the child that will be moved. if (false == IsLeaf) { var n = nodeManager.Find(leftSibling.childrenIds[leftSibling.childrenIds.Count - 1]); n.parentId = this.id; nodeManager.MarkAsChanged(n); // Then move it childrenIds.Insert(0, leftSibling.childrenIds[leftSibling.childrenIds.Count - 1]); leftSibling.childrenIds.RemoveAt(leftSibling.childrenIds.Count - 1); } // The tree is now balanced; nodeManager.MarkAsChanged(this); nodeManager.MarkAsChanged(parent); nodeManager.MarkAsChanged(leftSibling); return; } // Otherwise, if both immediate siblings have only the minimum number of elements, // then merge with a sibling sandwiching their separator taken off from their parent var leftChild = rightSibling != null ? this : leftSibling; var rightChild = rightSibling != null ? rightSibling : this; var seperatorParentIndex = rightSibling != null ? indexInParent : (indexInParent - 1); // Step 1: // Copy the separator to the end of the left node (the left node may be the // deficient node or it may be the sibling with the minimum number of elements) leftChild.entries.Add(parent.GetEntry(seperatorParentIndex)); // Move all elements from the right node to the left // node (the left node now has the maximum number of elements, and the right node – empty) leftChild.entries.AddRange(rightChild.entries); leftChild.childrenIds.AddRange(rightChild.childrenIds); // Update parent id of the children that has been moved from rightChild to leftChild foreach (var id in rightChild.childrenIds) { var n = nodeManager.Find(id); n.parentId = leftChild.id; nodeManager.MarkAsChanged(n);; } // Remove the separator from the parent along with its // empty right child (the parent loses an element) parent.entries.RemoveAt(seperatorParentIndex); parent.childrenIds.RemoveAt(seperatorParentIndex + 1); nodeManager.Delete(rightChild); // If the parent is the root and now has no elements, // then free it and make the merged node the new root (tree becomes shallower) if (parent.parentId == 0 && parent.EntriesCount == 0) { leftChild.parentId = 0; nodeManager.MarkAsChanged(leftChild); // Changed left one nodeManager.MakeRoot(leftChild); nodeManager.Delete(parent); // Deleted parent } // Otherwise, if the parent has fewer than // the required number of elements, then rebalance the parent else if ((parent.parentId != 0) && (parent.EntriesCount < nodeManager.MinEntriesPerNode)) { nodeManager.MarkAsChanged(leftChild); // Changed left one nodeManager.MarkAsChanged(parent); // Changed parent parent.Rebalance(); } else { nodeManager.MarkAsChanged(leftChild); // Changed left one nodeManager.MarkAsChanged(parent); // Changed parent } }