- btree insertion with tests
parent
9563b15010
commit
28b826151a
@ -0,0 +1,266 @@
|
||||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package btree implements a B tree.
|
||||
//
|
||||
// Structure is not thread safe.
|
||||
//
|
||||
// References: https://en.wikipedia.org/wiki/B-tree
|
||||
package btree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/emirpasic/gods/trees"
|
||||
"github.com/emirpasic/gods/utils"
|
||||
)
|
||||
|
||||
func assertTreeImplementation() {
|
||||
var _ trees.Tree = (*Tree)(nil)
|
||||
}
|
||||
|
||||
// Tree holds elements of the B-tree
|
||||
type Tree struct {
|
||||
root *Node // Root node
|
||||
comparator utils.Comparator // Key comparator
|
||||
size int // Total number of keys in the tree
|
||||
m int // Knuth order (maximum number of children)
|
||||
}
|
||||
|
||||
// Node is a single element within the tree
|
||||
type Node struct {
|
||||
parent *Node
|
||||
entries []*Entry // Contained keys in node
|
||||
children []*Node // Children nodes
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
key interface{}
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// NewWith instantiates a B-tree with the Knuth order (maximum number of children) and a custom key comparator.
|
||||
func NewWith(order int, comparator utils.Comparator) *Tree {
|
||||
if order < 2 {
|
||||
panic("Invalid order, should be at least 2")
|
||||
}
|
||||
return &Tree{m: order, comparator: comparator}
|
||||
}
|
||||
|
||||
// NewWithIntComparator instantiates a B-tree with the Knuth order (maximum number of children) and the IntComparator, i.e. keys are of type int.
|
||||
func NewWithIntComparator(order int) *Tree {
|
||||
return NewWith(order, utils.IntComparator)
|
||||
}
|
||||
|
||||
// NewWithStringComparator instantiates a B-tree with the Knuth order (maximum number of children) and the StringComparator, i.e. keys are of type string.
|
||||
func NewWithStringComparator(order int) *Tree {
|
||||
return NewWith(order, utils.StringComparator)
|
||||
}
|
||||
|
||||
// Put inserts key-value pair node into the tree.
|
||||
// If key already exists, then its value is updated with the new value.
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (tree *Tree) Put(key interface{}, value interface{}) {
|
||||
entry := &Entry{key: key, value: value}
|
||||
|
||||
if tree.root == nil {
|
||||
tree.root = &Node{entries: []*Entry{entry}, children: []*Node{}}
|
||||
tree.size++
|
||||
return
|
||||
}
|
||||
|
||||
if tree.insert(tree.root, entry) {
|
||||
tree.size++
|
||||
}
|
||||
}
|
||||
|
||||
// Get searches the node in the tree by key and returns its value or nil if key is not found in tree.
|
||||
// Second return parameter is true if key was found, otherwise false.
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (tree *Tree) Get(key interface{}) (value interface{}, found bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Remove remove the node from the tree by key.
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (tree *Tree) Remove(key interface{}) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Empty returns true if tree does not contain any nodes
|
||||
func (tree *Tree) Empty() bool {
|
||||
return tree.size == 0
|
||||
}
|
||||
|
||||
// Size returns number of nodes in the tree.
|
||||
func (tree *Tree) Size() int {
|
||||
return tree.size
|
||||
}
|
||||
|
||||
// Keys returns all keys in-order
|
||||
func (tree *Tree) Keys() []interface{} {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
// Values returns all values in-order based on the key.
|
||||
func (tree *Tree) Values() []interface{} {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
// Clear removes all nodes from the tree.
|
||||
func (tree *Tree) Clear() {
|
||||
tree.root = nil
|
||||
tree.size = 0
|
||||
}
|
||||
|
||||
// String returns a string representation of container
|
||||
func (tree *Tree) String() string {
|
||||
str := "BTree\n"
|
||||
if !tree.Empty() {
|
||||
str += tree.root.String()
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func (node *Node) String() string {
|
||||
return fmt.Sprintf("%v", node.entries)
|
||||
}
|
||||
|
||||
func (entry *Entry) String() string {
|
||||
return fmt.Sprintf("%v", entry.key)
|
||||
}
|
||||
|
||||
func (tree *Tree) isLeaf(node *Node) bool {
|
||||
return len(node.children) == 0
|
||||
}
|
||||
|
||||
func (tree *Tree) isFull(node *Node) bool {
|
||||
return len(node.entries) == tree.maxEntries()
|
||||
}
|
||||
|
||||
func (tree *Tree) shouldSplit(node *Node) bool {
|
||||
return len(node.entries) > tree.maxEntries()
|
||||
}
|
||||
|
||||
func (tree *Tree) maxChildren() int {
|
||||
return tree.m
|
||||
}
|
||||
|
||||
func (tree *Tree) maxEntries() int {
|
||||
return tree.m - 1
|
||||
}
|
||||
|
||||
func (tree *Tree) middle() int {
|
||||
return (tree.m - 1) / 2 // "-1" to favor right nodes to have more keys when splitting
|
||||
}
|
||||
|
||||
func (tree *Tree) search(node *Node, entry *Entry) (index int, found bool) {
|
||||
low, high := 0, len(node.entries) - 1
|
||||
var mid int
|
||||
for low <= high {
|
||||
mid = (high + low) / 2
|
||||
compare := tree.comparator(entry.key, node.entries[mid].key)
|
||||
switch {
|
||||
case compare > 0:
|
||||
low = mid + 1
|
||||
case compare < 0:
|
||||
high = mid - 1
|
||||
case compare == 0:
|
||||
return mid, true
|
||||
}
|
||||
}
|
||||
return low, false
|
||||
}
|
||||
|
||||
func (tree *Tree) insert(node *Node, entry *Entry) (inserted bool) {
|
||||
if tree.isLeaf(node) {
|
||||
return tree.insertIntoLeaf(node, entry)
|
||||
}
|
||||
return tree.insertIntoInternal(node, entry)
|
||||
}
|
||||
|
||||
func (tree *Tree) insertIntoLeaf(node *Node, entry *Entry) (inserted bool) {
|
||||
insertPosition, found := tree.search(node, entry)
|
||||
if found {
|
||||
node.entries[insertPosition] = nil // GC
|
||||
node.entries[insertPosition] = entry
|
||||
return false
|
||||
}
|
||||
node.entries = append(node.entries, nil)
|
||||
copy(node.entries[insertPosition + 1:], node.entries[insertPosition:])
|
||||
node.entries[insertPosition] = entry
|
||||
tree.split(node)
|
||||
return true
|
||||
}
|
||||
|
||||
func (tree *Tree) insertIntoInternal(node *Node, entry *Entry) (inserted bool) {
|
||||
insertPosition, found := tree.search(node, entry)
|
||||
if found {
|
||||
node.entries[insertPosition] = nil // GC
|
||||
node.entries[insertPosition] = entry
|
||||
return false
|
||||
}
|
||||
return tree.insert(node.children[insertPosition], entry)
|
||||
}
|
||||
|
||||
func (tree *Tree) split(node *Node) {
|
||||
if !tree.shouldSplit(node) {
|
||||
return
|
||||
}
|
||||
|
||||
if node == tree.root {
|
||||
tree.splitRoot()
|
||||
return
|
||||
}
|
||||
|
||||
tree.splitNonRoot(node)
|
||||
}
|
||||
|
||||
func (tree *Tree) splitNonRoot(node *Node) {
|
||||
middle := tree.middle()
|
||||
parent := node.parent
|
||||
|
||||
left := &Node{entries: node.entries[:middle], parent: parent}
|
||||
right := &Node{entries: node.entries[middle + 1:], parent: parent}
|
||||
|
||||
if !tree.isLeaf(node) {
|
||||
left.children = node.children[:middle + 1]
|
||||
right.children = node.children[middle + 1:]
|
||||
}
|
||||
|
||||
insertPosition, _ := tree.search(parent, node.entries[middle])
|
||||
parent.entries = append(parent.entries, nil)
|
||||
copy(parent.entries[insertPosition + 1:], parent.entries[insertPosition:])
|
||||
parent.entries[insertPosition] = node.entries[middle]
|
||||
|
||||
parent.children[insertPosition] = left
|
||||
|
||||
parent.children = append(parent.children, nil)
|
||||
copy(parent.children[insertPosition + 2:], parent.children[insertPosition + 1:])
|
||||
parent.children[insertPosition + 1] = right
|
||||
|
||||
node = nil // GC
|
||||
|
||||
tree.split(parent)
|
||||
}
|
||||
|
||||
func (tree *Tree) splitRoot() {
|
||||
middle := tree.middle()
|
||||
|
||||
left := &Node{entries: tree.root.entries[:middle]}
|
||||
right := &Node{entries: tree.root.entries[middle + 1:]}
|
||||
|
||||
if !tree.isLeaf(tree.root) {
|
||||
left.children = tree.root.children[:middle + 1]
|
||||
right.children = tree.root.children[middle + 1:]
|
||||
}
|
||||
|
||||
newRoot := &Node{
|
||||
entries: []*Entry{tree.root.entries[middle]},
|
||||
children: []*Node{left, right},
|
||||
}
|
||||
|
||||
left.parent = newRoot
|
||||
right.parent = newRoot
|
||||
tree.root = newRoot
|
||||
}
|
Loading…
Reference in New Issue