-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7f4a18d
commit 01695f5
Showing
1 changed file
with
330 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,330 @@ | ||
package bs_radix | ||
|
||
import ( | ||
"sort" | ||
"strings" | ||
"fmt" | ||
) | ||
|
||
|
||
// leafNode is used to represent a value | ||
type leafNode struct { | ||
key string | ||
val interface{} | ||
} | ||
|
||
// edge is used to represent an edge node | ||
type edge struct { | ||
label byte | ||
node *node | ||
} | ||
|
||
type node struct { | ||
// leaf is used to store possible leaf | ||
leaf *leafNode | ||
|
||
// prefix is the common prefix we ignore | ||
prefix string | ||
|
||
// Edges should be stored in-order for iteration. | ||
// We avoid a fully materialized slice to save memory, | ||
// since in most cases we expect to be sparse | ||
edges edges | ||
} | ||
|
||
func (n *node) isLeaf() bool { | ||
return n.leaf != nil | ||
} | ||
|
||
func (n *node) addEdge(e edge) { | ||
n.edges = append(n.edges, e) | ||
n.edges.Sort() | ||
} | ||
|
||
func (n *node) replaceEdge(e edge) { | ||
num := len(n.edges) | ||
idx := sort.Search(num, func(i int) bool { | ||
return n.edges[i].label >= e.label | ||
}) | ||
if idx < num && n.edges[idx].label == e.label { | ||
n.edges[idx].node = e.node | ||
return | ||
} | ||
panic("replacing missing edge") | ||
} | ||
|
||
func (n *node) getEdge(label byte) *node { | ||
num := len(n.edges) | ||
idx := sort.Search(num, func(i int) bool { | ||
return n.edges[i].label >= label | ||
}) | ||
if idx < num && n.edges[idx].label == label { | ||
return n.edges[idx].node | ||
} | ||
return nil | ||
} | ||
|
||
func (n *node) delEdge(label byte) { | ||
num := len(n.edges) | ||
idx := sort.Search(num, func(i int) bool { | ||
return n.edges[i].label >= label | ||
}) | ||
if idx < num && n.edges[idx].label == label { | ||
copy(n.edges[idx:], n.edges[idx+1:]) | ||
n.edges[len(n.edges)-1] = edge{} | ||
n.edges = n.edges[:len(n.edges)-1] | ||
} | ||
} | ||
|
||
type edges []edge | ||
|
||
func (e edges) Len() int { | ||
return len(e) | ||
} | ||
|
||
func (e edges) Less(i, j int) bool { | ||
return e[i].label < e[j].label | ||
} | ||
|
||
func (e edges) Swap(i, j int) { | ||
e[i], e[j] = e[j], e[i] | ||
} | ||
|
||
func (e edges) Sort() { | ||
sort.Sort(e) | ||
} | ||
|
||
// Tree implements a radix tree. This can be treated as a | ||
// Dictionary abstract data type. The main advantage over | ||
// a standard hash map is prefix-based lookups and | ||
// ordered iteration, | ||
type Tree struct { | ||
root *node | ||
size int | ||
} | ||
|
||
// New returns an empty Tree | ||
func New() *Tree { | ||
t := &Tree{root: &node{}} | ||
return t | ||
} | ||
|
||
// Len is used to return the number of elements in the tree | ||
func (t *Tree) Len() int { | ||
return t.size | ||
} | ||
|
||
// longestPrefix finds the length of the shared prefix | ||
// of two strings | ||
func longestPrefix(k1, k2 string) int { | ||
max := len(k1) | ||
if l := len(k2); l < max { | ||
max = l | ||
} | ||
var i int | ||
for i = 0; i < max; i++ { | ||
if k1[i] != k2[i] { | ||
break | ||
} | ||
} | ||
return i | ||
} | ||
|
||
// Insert is used to add a newentry or update | ||
// an existing entry. Returns if updated. | ||
func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) { | ||
var parent *node | ||
n := t.root | ||
search := s | ||
for { | ||
// Handle key exhaution | ||
if len(search) == 0 { | ||
if n.isLeaf() { | ||
old := n.leaf.val | ||
n.leaf.val = v | ||
return old, true | ||
} | ||
|
||
n.leaf = &leafNode{ | ||
key: s, | ||
val: v, | ||
} | ||
t.size++ | ||
return nil, false | ||
} | ||
|
||
// Look for the edge | ||
parent = n | ||
n = n.getEdge(search[0]) | ||
|
||
// No edge, create one | ||
if n == nil { | ||
e := edge{ | ||
label: search[0], | ||
node: &node{ | ||
leaf: &leafNode{ | ||
key: s, | ||
val: v, | ||
}, | ||
prefix: search, | ||
|
||
}, | ||
} | ||
fmt.Println("Prefix: ", search) | ||
parent.addEdge(e) | ||
t.size++ | ||
return nil, false | ||
} | ||
|
||
// Determine longest prefix of the search key on match | ||
commonPrefix := longestPrefix(search, n.prefix) | ||
if commonPrefix == len(n.prefix) { | ||
search = search[commonPrefix:] | ||
continue | ||
} | ||
|
||
// Split the node | ||
t.size++ | ||
child := &node{ | ||
prefix: search[:commonPrefix], | ||
|
||
} | ||
fmt.Println("Prefix: ", search[:commonPrefix]) | ||
parent.replaceEdge(edge{ | ||
label: search[0], | ||
node: child, | ||
}) | ||
|
||
// Restore the existing node | ||
child.addEdge(edge{ | ||
label: n.prefix[commonPrefix], | ||
node: n, | ||
}) | ||
n.prefix = n.prefix[commonPrefix:] | ||
fmt.Println("Prefix: ", n.prefix[commonPrefix:]) | ||
|
||
// Create a new leaf node | ||
leaf := &leafNode{ | ||
key: s, | ||
val: v, | ||
} | ||
|
||
// If the new key is a subset, add to to this node | ||
search = search[commonPrefix:] | ||
if len(search) == 0 { | ||
child.leaf = leaf | ||
return nil, false | ||
} | ||
|
||
// Create a new edge for the node | ||
child.addEdge(edge{ | ||
label: search[0], | ||
node: &node{ | ||
leaf: leaf, | ||
prefix: search, | ||
|
||
}, | ||
|
||
}) | ||
fmt.Println("Prefix: ", search) | ||
return nil, false | ||
} | ||
} | ||
|
||
// Delete is used to delete a key, returning the previous | ||
// value and if it was deleted | ||
func (t *Tree) Delete(s string) (interface{}, bool) { | ||
var parent *node | ||
var label byte | ||
n := t.root | ||
search := s | ||
for { | ||
// Check for key exhaution | ||
if len(search) == 0 { | ||
if !n.isLeaf() { | ||
break | ||
} | ||
goto DELETE | ||
} | ||
|
||
// Look for an edge | ||
parent = n | ||
label = search[0] | ||
n = n.getEdge(label) | ||
if n == nil { | ||
break | ||
} | ||
|
||
// Consume the search prefix | ||
if strings.HasPrefix(search, n.prefix) { | ||
search = search[len(n.prefix):] | ||
} else { | ||
break | ||
} | ||
} | ||
return nil, false | ||
|
||
DELETE: | ||
// Delete the leaf | ||
leaf := n.leaf | ||
n.leaf = nil | ||
t.size-- | ||
|
||
// Check if we should delete this node from the parent | ||
if parent != nil && len(n.edges) == 0 { | ||
parent.delEdge(label) | ||
} | ||
|
||
// Check if we should merge this node | ||
if n != t.root && len(n.edges) == 1 { | ||
n.mergeChild() | ||
} | ||
|
||
// Check if we should merge the parent's other child | ||
if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { | ||
parent.mergeChild() | ||
} | ||
|
||
return leaf.val, true | ||
} | ||
|
||
func (n *node) mergeChild() { | ||
e := n.edges[0] | ||
child := e.node | ||
n.prefix = n.prefix + child.prefix | ||
n.leaf = child.leaf | ||
n.edges = child.edges | ||
} | ||
|
||
// Get is used to lookup a specific key, returning | ||
// the value and if it was found | ||
func (t *Tree) Get(s string) (interface{}, bool) { | ||
n := t.root | ||
search := s | ||
for { | ||
// Check for key exhaution | ||
if len(search) == 0 { | ||
if n.isLeaf() { | ||
return n.leaf.val, true | ||
} | ||
break | ||
} | ||
|
||
// Look for an edge | ||
n = n.getEdge(search[0]) | ||
if n == nil { | ||
break | ||
} | ||
|
||
// Consume the search prefix | ||
if strings.HasPrefix(search, n.prefix) { | ||
search = search[len(n.prefix):] | ||
} else { | ||
break | ||
} | ||
} | ||
return nil, false | ||
} | ||
|
||
|
||
|