A B-Tree is a self-balancing search tree, in which nodes can have more than two children.
A B-Tree of order n satisfies the following properties:
A second order B-Tree with keys from 1 to 20 looks like this:
class BTreeNode<Key: Comparable, Value> {
unowned var owner: BTree<Key, Value>
fileprivate var keys = [Key]()
var children: [BTreeNode]?
...
}
The main parts of a node are two arrays:
Nodes also have a reference to the tree they belong to.
This is necessary because nodes have to know the order of the tree.
Note: The array containing the children is an Optional, because leaf nodes don't have children.
k
begins at the root node.l
that is not less than k
,k == l
then we have found the key.k < l
:
l
, and perform the steps 3 - 5 again.k
is not in the tree.k
is not in the tree.value(for:)
method searches for the given key and if it's in the tree,
it returns the value associated with it, else it returns nil
.
Keys can only be inserted to leaf nodes.
k
we want to insert.l
which we are standing on is greater than k
:k
to the position before l
.k
to the position after l
.After insertion we should check if the number of keys in the node is in the correct range.
If there are more keys in the node than order*2
, we need to split the node.
After splitting a node it is possible that the parent node will also contain too many keys, so we need to split it also.
In the worst case the splitting goes up to the root (in this case the height of the tree increases).
An insertion to a first order tree looks like this:
The method insert(_:for:)
does the insertion.
After it has inserted a key, as the recursion goes up every node checks the number of keys in its child.
if a node has too many keys, its parent calls the split(child:atIndex:)
method on it.
The root node is checked by the tree itself.
If the root has too many nodes after the insertion the tree calls the splitRoot()
method.
Keys can only be removed from leaf nodes.
k
we want to remove.k
with its inorder predecessor p
, then we remove p
from the leaf node.After a key have been removed from a node we should check that the node has enough keys.
If a node has fewer keys than the order of the tree, then we should move a key to it,
or merge it with one of its siblings.
If the problematic node has a nearest sibling that has more keys than the order of the tree,
we should perform this operation on the tree, else we should merge the node with one of its siblings.
Let's say the child we want to fix c1
is at index i
in its parent node's children array.
If the child c2
at index i-1
has more keys than the order of the tree:
i-1
from the parent node to the child c1
's keys array at index 0
.c2
to the parent's keys array at index i-1
.c1
is non-leaf) We move the last child from c2
to c1
's children array at index 0.Else:
i
from the parent node to the end of child c1
's keys array.c2
to the parent's keys array at index i
.c1
isn't a leaf) We move the first child from c2
to the end of c1
's children array.####Merging two nodes
Let's say we want to merge the child c1
at index i
in its parent's children array.
If c1
has a left sibling c2
:
i-1
to the end of c2
's keys array.c1
to the end of c2
's keys and children array.i-1
from the parent node.Else if c1
only has a right sibling c2
:
i
to the beginning of c2
's keys array.c1
to the beginning of c2
's keys and children array.i
from the parent node.After merging it is possible that now the parent node contains too few keys,
so in the worst case merging also can go up to the root, in which case the height of the tree decreases.
remove(_:)
method removes the given key from the tree. After a key has been deleted,
every node checks the number of keys in its child. If a child has less nodes than the order of the tree,
it calls the fix(childWithTooFewKeys:atIndex:)
method.
fix(childWithTooFewKeys:atIndex:)
method decides which way to fix the child (by moving a key to it,
or by merging it), then calls move(keyAtIndex:to:from:at:)
or
merge(child:atIndex:to:)
method according to its choice.
Written for Swift Algorithm Club by Viktor Szilárd Simkó