Add radix locally

This commit is contained in:
Miek Gieben 2012-09-04 20:08:38 +02:00
parent d2546dcbd7
commit e4f67647b9
2 changed files with 465 additions and 0 deletions

311
radix.go Normal file
View File

@ -0,0 +1,311 @@
// Package radix implements a radix tree.
//
// A radix tree is defined in:
// Donald R. Morrison. "PATRICIA -- practical algorithm to retrieve
// information coded in alphanumeric". Journal of the ACM, 15(4):514-534,
// October 1968
//
// Also see http://en.wikipedia.org/wiki/Radix_tree for more information.
//
package radix
// Radix represents a radix tree.
// The key of the root node of a tree is always empty.
type Radix struct {
// children maps the first letter of each child to the child.
children map[byte]*Radix
key string
parent *Radix // a pointer back to the parent
// The contents of the radix node.
Value interface{}
}
func (r *Radix) String() string {
return r.stringHelper("")
}
func (r *Radix) stringHelper(indent string) (s string) {
s = indent + r.Key() + ":"
if r.Value == nil {
s = indent + "<nil>:"
}
for i, _ := range r.children {
s += string(i)
}
s += "\n"
for i, r1 := range r.children {
s += indent + string(i) + ":" + r1.stringHelper(" "+indent)
}
return s
}
// Key returns the full (from r down to this node) key under which r is stored.
func (r *Radix) Key() (s string) {
for p := r; p != nil; p = p.parent {
s = p.key + s
}
return
}
func longestCommonPrefix(key, bar string) (string, int) {
if key == "" || bar == "" {
return "", 0
}
x := 0
for key[x] == bar[x] {
x = x + 1
if x == len(key) || x == len(bar) {
break
}
}
return key[:x], x // == bar[:x]
}
// Insert inserts the value into the tree with the specified key. It returns the radix node
// it just inserted. Insert must be called on the root of the tree.
func (r *Radix) Insert(key string, value interface{}) *Radix {
// look up the child starting with the same letter as key
// if there is no child with the same starting letter, insert a new one
child, ok := r.children[key[0]]
if !ok {
r.children[key[0]] = &Radix{make(map[byte]*Radix), key, r, value}
return r.children[key[0]]
}
if key == child.key {
child.Value = value
return child
}
commonPrefix, prefixEnd := longestCommonPrefix(key, child.key)
if commonPrefix == child.key {
return child.Insert(key[prefixEnd:], value)
}
// create new child node to replace current child
newChild := &Radix{make(map[byte]*Radix), commonPrefix, r, nil}
// replace child of current node with new child: map first letter of common prefix to new child
r.children[commonPrefix[0]] = newChild
// shorten old key to the non-shared part
child.key = child.key[prefixEnd:]
// map old child's new first letter to old child as a child of the new child
newChild.children[child.key[0]] = child
child.parent = newChild // update the pointer of the current child which is moved down
// if there are key left of key, insert them into our new child
if key != newChild.key {
newChild.Insert(key[prefixEnd:], value)
} else {
newChild.Value = value
}
return newChild
}
// Find returns the node associated with key. All childeren of this node share the same prefix,
// r does not have to be the root of the radix tree, but it starts be looking at the children
// of the current node.
func (r *Radix) Find(key string) *Radix {
if key == "" {
return nil
}
child, ok := r.children[key[0]]
if !ok {
return nil
}
if key == child.key {
return child
}
commonPrefix, prefixEnd := longestCommonPrefix(key, child.key)
// if child.key is not completely contained in key, abort [e.g. trying to find "ab" in "abc"]
if child.key != commonPrefix {
return nil
}
// find the key left of key in child
return child.Find(key[prefixEnd:])
}
// Find predecessor: Locates the largest string less than a given string, by lexicographic order.
// Predecessor returns the node who's key is the largest, but always smaller than the given key.
// If nothing is found nil is returned.
func (r *Radix) Predecessor(key string) *Radix {
child, ok := r.children[key[0]]
if !ok {
for r.Value == nil {
if r.parent == nil {
return nil // Root node
}
r = r.parent
}
return r
}
// Ok, we found the node...
if key == child.key {
for r.Value == nil {
if r.parent == nil {
return nil // Root node
}
r = r.parent
}
return r
}
commonPrefix, prefixEnd := longestCommonPrefix(key, child.key)
// if child.key is not completely contained in key, return the parent
if child.key != commonPrefix {
for r.Value == nil {
if r.parent == nil {
return nil // Root node
}
r = r.parent
}
return r
}
// find the key left of key in child
return child.Predecessor(key[prefixEnd:])
}
// Find successor: Locates the smallest string greater than a given string, by
// Prefix returns a slice with all the keys that share this prefix. Prefix
// needs to start from the root node.
func (r *Radix) Prefix(prefix string) []string {
bestfit := r.prefix(prefix)
if bestfit == nil {
return nil
}
return bestfit.Keys()
}
func (r *Radix) prefix(prefix string) *Radix {
if r.key == prefix {
return r
}
child, ok := r.children[prefix[0]]
if !ok {
return nil
}
if prefix == child.key {
return child
}
// The whole of the prefix is contained in the child's key
_, prefixEnd := longestCommonPrefix(prefix, child.key)
if prefixEnd+1 > len(prefix) {
return child
}
return child.prefix(prefix[prefixEnd:])
}
// Up returns the first node above r which has a non-nil Value.
// If nothing is found nil is returned.
func (r *Radix) Up() *Radix {
if r.parent == nil {
return nil
}
// Walk until you can walk nomore
for r = r.parent; r != nil && r.Value == nil; r = r.parent {
// ...
}
return r
}
// Remove removes any value set to key. It returns the removed node or nil if the
// node cannot be found.
func (r *Radix) Remove(key string) *Radix {
child, ok := r.children[key[0]]
if !ok {
return nil
}
// if the correct end node is found...
if key == child.key {
switch len(child.children) {
case 0:
// remove child from current node if child has no children on its own
delete(r.children, key[0])
case 1:
// since len(child.children) == 1, there is only one subchild; we have to use range to get the value, though, since we do not know the key
for _, subchild := range child.children {
// essentially moves the subchild up one level to replace the child we want to delete, while keeping the key of child
child.key = child.key + subchild.key
child.Value = subchild.Value
child.children = subchild.children
child.parent = r
}
default:
// if there are >= 2 subchilds, we can only set the value to nil, thus delete any value set to key
child.Value = nil
}
return child
}
// Node has not been foundJ, key != child.keys
commonPrefix, prefixEnd := longestCommonPrefix(key, child.key)
// if child.key is not completely contained in key, abort [e.g. trying to delete "ab" from "abc"]
if child.key != commonPrefix {
return nil
}
// else: cut off common prefix and delete left string in child
return child.Remove(key[prefixEnd:])
}
// Do calls function f on each node in the tree. f's parameter will be r.Value. The behavior of Do is
// undefined if f changes r.
func (r *Radix) Do(f func(interface{})) {
if r != nil {
f(r.Value)
for _, child := range r.children {
child.Do(f)
}
}
}
// Len computes the number of nodes in the radix tree r.
func (r *Radix) Len() int {
i := 0
if r != nil {
if r.Value != nil {
i++
}
for _, child := range r.children {
i += child.Len()
}
}
return i
}
// Keys return all the keys from the node r and downwards
func (r *Radix) Keys() (s []string) {
// get the full key for this node and use that to get all the other keys
fullkey := r.key
for p := r.parent; p != nil; p = p.parent {
fullkey = p.key + fullkey
}
return r.keys(fullkey)
}
func (r *Radix) keys(fullkey string) (s []string) {
if fullkey != "" { // root
s = append(s, fullkey)
}
for _, c := range r.children {
s = append(s, c.keys(fullkey+c.key)...)
}
return s
}
// New returns an initialized radix tree.
func New() *Radix {
return &Radix{make(map[byte]*Radix), "", nil, nil}
}

154
radix_test.go Normal file
View File

@ -0,0 +1,154 @@
package radix
import (
"fmt"
"testing"
)
func printit(r *Radix, level int) {
for i := 0; i < level; i++ {
fmt.Print("\t")
}
fmt.Printf("%p '%v' value: '%v' parent %p\n", r, r.key, r.Value, r.parent)
for _, child := range r.children {
printit(child, level+1)
}
}
func radixtree() *Radix {
r := New()
r.Insert("test", "a")
r.Insert("tester", "a")
r.Insert("team", "a")
r.Insert("te", "a")
return r
}
// None, of the childeren must have a prefix incommon with r.key
func validate(r *Radix) bool {
return true
for _, child := range r.children {
_, i := longestCommonPrefix(r.key, child.key)
if i != 0 {
return false
}
validate(child)
}
return true
}
func TestPredecessor(t *testing.T) {
r := radixtree()
r.Insert("teak", "a")
printit(r, 0)
// team is below te, so we should find 'te'
if x := r.Predecessor("team").Key(); x != "te" {
t.Logf("Failed to find predecessor of team, found %s", x)
t.Fail()
}
// tester is there, so we look for testeraaa
if r.Predecessor("testeraaa").Key() != "tester" {
t.Logf("Failed to find predecessor of testeraaa")
t.Fail()
}
if r.Predecessor("testeraaahsahsjahsj").Key() != "tester" {
t.Logf("Failed to find predecessor of testeraaa...")
t.Fail()
}
// this should find nothing, or at least stop at the root node
if r.Predecessor("atester").Key() != "" {
t.Logf("Found predecessor of atester which shouldn't be there")
t.Fail()
}
}
func TestPrint(t *testing.T) {
// TODO(mg): fix
}
func TestInsert(t *testing.T) {
r := New()
if !validate(r) {
t.Log("Tree does not validate")
t.Fail()
}
if r.Len() != 0 {
t.Log("Len should be 0", r.Len())
}
r.Insert("test", nil)
r.Insert("slow", nil)
r.Insert("water", nil)
r.Insert("tester", nil)
r.Insert("testering", nil)
r.Insert("rewater", nil)
r.Insert("waterrat", nil)
if !validate(r) {
t.Log("Tree does not validate")
t.Fail()
}
}
func TestRemove(t *testing.T) {
r := New()
r.Insert("test", "aa")
r.Insert("slow", "bb")
if k := r.Remove("slow").Value; k != "bb" {
t.Log("should be bb", k)
t.Fail()
}
if r.Remove("slow") != nil {
t.Log("should be nil")
t.Fail()
}
r.Insert("test", "aa")
r.Insert("tester", "aa")
r.Insert("testering", "aa")
r.Find("tester").Remove("test")
}
func TestKeys(t *testing.T) {
r := radixtree()
i := 0
for _, k := range r.Keys() {
if k == "" { i++ }
if k == "te" { i++ }
if k == "team" { i++ }
if k == "test" { i++ }
if k == "tester" { i++ }
}
if i != 5 {
t.Fatal("not all keys seen")
}
}
func ExampleFind() {
r := New()
r.Insert("tester", nil)
r.Insert("testering", nil)
r.Insert("te", nil)
r.Insert("testeringandmore", nil)
iter(r.Find("tester"))
// Output:
// prefix tester
// prefix testering
// prefix testeringandmore
}
func iter(r *Radix) {
fmt.Printf("prefix %s\n", r.Key())
for _, child := range r.children {
iter(child)
}
}
func BenchmarkFind(b *testing.B) {
b.StopTimer()
r := radixtree()
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = r.Find("tester")
}
}