mirror of https://github.com/emirpasic/gods
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
7.4 KiB
Go
292 lines
7.4 KiB
Go
// 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 (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/emirpasic/gods/trees"
|
|
"github.com/emirpasic/gods/utils"
|
|
"strings"
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
// Entry represents the key-value pair contained within 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
|
|
}
|
|
|
|
// Height returns the height of the tree.
|
|
func (tree *Tree) Height() int {
|
|
return tree.Root.height()
|
|
}
|
|
|
|
// String returns a string representation of container (for debugging purposes)
|
|
func (tree *Tree) String() string {
|
|
var buffer bytes.Buffer
|
|
buffer.WriteString("BTree\n")
|
|
if !tree.Empty() {
|
|
tree.output(&buffer, tree.Root, 0, true)
|
|
}
|
|
return buffer.String()
|
|
}
|
|
|
|
func (entry *Entry) String() string {
|
|
return fmt.Sprintf("%v", entry.Key)
|
|
}
|
|
|
|
func (tree *Tree) output(buffer *bytes.Buffer, node *Node, level int, isTail bool) {
|
|
for e := 0; e < len(node.Entries)+1; e++ {
|
|
if e < len(node.Children) {
|
|
buffer.WriteString(strings.Repeat(" ", level))
|
|
tree.output(buffer, node.Children[e], level+1, true)
|
|
}
|
|
if e < len(node.Entries) {
|
|
buffer.WriteString(strings.Repeat(" ", level))
|
|
buffer.WriteString(fmt.Sprintf("%v", node.Entries[e].Key) + "\n")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (node *Node) height() int {
|
|
height := 0
|
|
for ; node != nil; node = node.Children[0] {
|
|
height++
|
|
if len(node.Children) == 0 {
|
|
break
|
|
}
|
|
}
|
|
return height
|
|
}
|
|
|
|
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] = 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] = 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
|
|
|
|
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
|
|
}
|