public TwoFourTreeNode Split() { Debug.Assert(IsKeyFull); var node = new TwoFourTreeNode(IsLeafNode) { _items = { [0] = _items[MaxKeyNum - 1] }, _keyNum = 1, Parent = Parent }; #if DEBUG Array.Clear(_items, 1, MaxKeyNum - 1); #endif if (!IsLeafNode) { Array.Copy(_children, MaxKeyNum - 1, node._children, 0, 2); for (var i = 0; i <= node._keyNum; i++) { node._children[i].Parent = node; } #if DEBUG Array.Clear(_children, MaxKeyNum - 1, 2); #endif } _keyNum = 1; return(node); }
public void Clear() { _root.Clear(); _root = new TwoFourTreeNode(true); _count = 0; _version++; }
public TwoFourTreeNode(KeyValuePair <TKey, TValue> item, TwoFourTreeNode child1, TwoFourTreeNode child2) : this(false) { _items[0] = item; _children[0] = child1; _children[1] = child2; child1.Parent = this; child2.Parent = this; _keyNum = 1; }
public void PushFrontItemChild(KeyValuePair <TKey, TValue> item, TwoFourTreeNode node = null) { Debug.Assert(!IsKeyFull); Debug.Assert(node != null && !IsLeafNode || node == null && IsLeafNode); if (_keyNum > 0) { Array.Copy(_items, 0, _items, 1, _keyNum); } _items[0] = item; if (node != null) { Array.Copy(_children, 0, _children, 1, _keyNum + 1); _children[0] = node; node.Parent = this; } _keyNum++; }
public void MergeWithRight(int rightIndex, TwoFourTreeNode right) { Debug.Assert(_keyNum == 0 && right._keyNum == 1 || _keyNum == 1 && right._keyNum == 0); Debug.Assert(Parent != null); Debug.Assert(Parent == right.Parent); Debug.Assert(Parent.Children[rightIndex] == right); var pair = Parent.RemoveItemChild(rightIndex - 1); AppendItemChild(pair.Item, IsLeafNode ? null : right._children[0]); if (right._keyNum != 0) { AppendItemChild(right._items[0], IsLeafNode ? null : right._children[1]); } right.Invalidate(); }
private void RemoveItem(TwoFourTreeNode node, int index) { /* * To remove a value from the 2–3–4 tree: * 1. Find the element to be deleted. * • If the element is not in a leaf node, remember its location and continue searching until a leaf, which will contain the element’s successor, is reached. * The successor can be either the largest key that is smaller than the one to be removed, or the smallest key that is larger than the one to be removed. * It is simplest to make adjustments to the tree from the top down such that the leaf node found is not a 2-node. That way, after the swap, there will not be an empty leaf node. * • If the element is in a 2-node leaf, just make the adjustments below. * Make the following adjustments when a 2-node – except the root node – is encountered on the way to the leaf we want to remove: * 1. If a sibling on either side of this node is a 3-node or a 4-node (thus having more than 1 key), perform a rotation with that sibling: * • The key from the other sibling closest to this node moves up to the parent key that overlooks the two nodes. * • The parent key moves down to this node to form a 3-node. * • The child that was originally with the rotated sibling key is now this node's additional child. * 2. If the parent is a 2-node and the sibling is also a 2-node, combine all three elements to form a new 4-node and shorten the tree. (This rule can only trigger if the parent 2-node is the root, * since all other 2-nodes along the way will have been modified to not be 2-nodes. This is why "shorten the tree" here preserves balance; this is also an important assumption for the fusion operation.) * 3. If the parent is a 3-node or a 4-node and all adjacent siblings are 2-nodes, do a fusion operation with the parent and an adjacent sibling: * • The adjacent sibling and the parent key overlooking the two sibling nodes come together to form a 4-node. * • Transfer the sibling's children to this node. * Once the sought value is reached, it can now be placed at the removed entry's location without a problem because we have ensured that the leaf node has more than 1 key. */ if (node.IsLeafNode) { node.RemoveItem(index); if (node.KeyNum == 0 && node != _root) { Debug.Assert(node.Parent != null); AdjustNodeWhenRemove(node); // if (node.GetChildIndex() < 0) node.Invalidate(); } _count--; _version++; } else { var successor = node.Children[index].GetMaxLeafNode(); var item = successor.Items[successor.KeyNum - 1]; node.Items[index] = item; RemoveItem(successor, successor.KeyNum - 1); } }
private void Split(TwoFourTreeNode node) { var parent = node.Parent; Debug.Assert(node.IsKeyFull); var midKey = node.Items[1]; var newNode = node.Split(); if (node == _root) { Debug.Assert(parent == null); var newRoot = new TwoFourTreeNode(midKey, node, newNode); _root = newRoot; } else { Debug.Assert(parent != null); var nodeIndex = node.GetChildIndex(); Debug.Assert(parent.Children[nodeIndex] == node); parent.InsertItemChild(nodeIndex, midKey, newNode); } }
public void InsertItemChild(int index, KeyValuePair <TKey, TValue> item, TwoFourTreeNode node = null) { Debug.Assert(!IsKeyFull); Debug.Assert(index >= 0 && index <= _keyNum); Debug.Assert(node != null && !IsLeafNode || node == null && IsLeafNode); var num = _keyNum - index; if (num > 0) { Array.Copy(_items, index, _items, index + 1, num); if (node != null) { Array.Copy(_children, index + 1, _children, index + 2, num); } } _items[index] = item; if (node != null) { _children[index + 1] = node; node.Parent = this; } _keyNum++; }
public void AppendItemChild(KeyValuePair <TKey, TValue> item, TwoFourTreeNode node = null) => InsertItemChild(_keyNum, item, node);
private void AdjustNodeWhenRemove(TwoFourTreeNode node) { var parent = node.Parent; Debug.Assert(parent != null); var childIndex = node.GetChildIndex(); var leftSiblingIndex = childIndex - 1; var rightSiblingIndex = childIndex + 1; // If a sibling on either side of this node is a 3-node or a 4-node (thus having more than 1 key) if (rightSiblingIndex <= parent.KeyNum && !parent.Children[rightSiblingIndex].IsKeyMin) { var sibling = parent.Children[rightSiblingIndex]; var pair = sibling.RemoveFirstItemChild(); node.AppendItemChild(parent.Items[childIndex], pair.Node); parent.Items[childIndex] = pair.Item; } else if (leftSiblingIndex >= 0 && !parent.Children[leftSiblingIndex].IsKeyMin) { var sibling = parent.Children[leftSiblingIndex]; var pair = sibling.RemoveLastItemChild(); node.PushFrontItemChild(parent.Items[leftSiblingIndex], pair.Node); parent.Items[leftSiblingIndex] = pair.Item; } // If all adjacent siblings are 2-nodes else if (rightSiblingIndex <= parent.KeyNum && parent.Children[rightSiblingIndex].IsKeyMin) { node.MergeWithRight(rightSiblingIndex, parent.Children[rightSiblingIndex]); if (parent.KeyNum == 0) { if (parent == _root) { _root.Invalidate(); _root = node; _root.Parent = null; } else { AdjustNodeWhenRemove(parent); } } } else if (leftSiblingIndex >= 0 && parent.Children[leftSiblingIndex].IsKeyMin) { var sibling = parent.Children[leftSiblingIndex]; sibling.MergeWithRight(childIndex, node); if (parent.KeyNum == 0) { if (parent == _root) { _root.Invalidate(); _root = sibling; _root.Parent = null; } else { AdjustNodeWhenRemove(parent); } } } else { throw new Exception("cannot reach here!"); } }