add trie/bst
This commit is contained in:
parent
04b27dc5df
commit
c3ea064bf0
404
internal/search/binary.go
Normal file
404
internal/search/binary.go
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
|
package search
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"cmp"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BSTDATAOFFSET = 1
|
||||||
|
BSTROOTNODEID = 0
|
||||||
|
LEFTOFFSET = 1
|
||||||
|
RIGHTOFFSET = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Binary Search Tree using a slice
|
||||||
|
*/
|
||||||
|
|
||||||
|
type Binary[Key cmp.Ordered] []Key
|
||||||
|
|
||||||
|
type BinaryTree[Key cmp.Ordered] struct {
|
||||||
|
*Binary[Key]
|
||||||
|
zeroIndex int
|
||||||
|
least, greatest, lastNode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBinaryTree[Key cmp.Ordered](capacity int) (bst *BinaryTree[Key]) {
|
||||||
|
tree := make(Binary[Key], 1, capacity)
|
||||||
|
bst = &BinaryTree[Key]{ Binary: &tree }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Empty() Key {
|
||||||
|
return (*b)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) IsInternal(index int) bool {
|
||||||
|
childIdx := 2 * index
|
||||||
|
return ! b.IsEmpty(childIdx + LEFTOFFSET) && ! b.IsEmpty(childIdx + RIGHTOFFSET)
|
||||||
|
//return (*b)[leftIdx] != (*b)[0] && (*b)[leftIdx + 1] != (*b)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) IsRight(index int) bool {
|
||||||
|
return index % 2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Left(index int) int {
|
||||||
|
return (2 * index) + LEFTOFFSET
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Right(index int) int {
|
||||||
|
return (2 * index) + RIGHTOFFSET
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Get(index int) (Key) {
|
||||||
|
return (*b)[index + BSTDATAOFFSET]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Set(index int, v Key) {
|
||||||
|
(*b)[index + BSTDATAOFFSET] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) Set(index int, v Key) {
|
||||||
|
b.Binary.Set(index, v)
|
||||||
|
if v == b.Empty() {
|
||||||
|
b.setZeroIndex(index)
|
||||||
|
}
|
||||||
|
b.setLastNode(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) SetEmpty(index int) {
|
||||||
|
(*b)[index + BSTDATAOFFSET] = b.Empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) SetEmpty(index int) {
|
||||||
|
b.Binary.SetEmpty(index)
|
||||||
|
if index == b.zeroIndex {
|
||||||
|
b.setZeroIndex(-1)
|
||||||
|
}
|
||||||
|
b.setLastNode(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) SetEmptyRange(start int, end int) {
|
||||||
|
empty := b.Empty()
|
||||||
|
for i := start; i <= end; i++ {
|
||||||
|
(*b)[i + BSTDATAOFFSET] = empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) SetEmptyRange(start int, end int) {
|
||||||
|
b.Binary.SetEmptyRange(start, end)
|
||||||
|
if b.zeroIndex >= start && b.zeroIndex <= end {
|
||||||
|
b.setZeroIndex(-1)
|
||||||
|
}
|
||||||
|
b.setLastNode(start)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) setZeroIndex(index int) {
|
||||||
|
b.zeroIndex = index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) setLastNode(index int) {
|
||||||
|
b.lastNode = max(b.lastNode, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[key]) LastNode() int {
|
||||||
|
return b.lastNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[key]) MaxDepth() int {
|
||||||
|
return b.Depth(b.lastNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (b *Binary[Key]) Median(index int) {
|
||||||
|
|
||||||
|
length := len(*b)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Copy(from int, to int) {
|
||||||
|
(*b)[to + BSTDATAOFFSET] = (*b)[from + BSTDATAOFFSET]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) Copy(from, to int) {
|
||||||
|
b.Binary.Copy(from, to)
|
||||||
|
if b.zeroIndex == from {
|
||||||
|
b.setZeroIndex(to)
|
||||||
|
}
|
||||||
|
slog.Info("Copy()", "from", from, "to", to, "value", b.Get(from), "zero", b.zeroIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) LevelRange(index int, treeIndex int) (start int, end int) {
|
||||||
|
treeDepth := b.Depth(treeIndex)
|
||||||
|
depth := b.Depth(index)
|
||||||
|
diff := depth - treeDepth
|
||||||
|
switch diff {
|
||||||
|
case 0:
|
||||||
|
if index == treeIndex {
|
||||||
|
return index, index
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
leftIdx := (2 * treeIndex) + 1
|
||||||
|
if index >= leftIdx && index <= leftIdx + 1 {
|
||||||
|
return leftIdx, leftIdx + 1
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
exp := int(math.Exp2(float64(diff)))
|
||||||
|
level := exp * treeIndex
|
||||||
|
childOffset := exp - 1
|
||||||
|
|
||||||
|
left := level + childOffset
|
||||||
|
right := level + (childOffset * 2)
|
||||||
|
return left, right
|
||||||
|
}
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) InSubTree(index, treeIndex int) bool {
|
||||||
|
if treeIndex > index {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
left, right := b.LevelRange(index, treeIndex)
|
||||||
|
if index >= left && index <= right {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) MoveSubTree(from, to int) {
|
||||||
|
if ! b.IsLeaf(to) {
|
||||||
|
panic("this move would overwrite the target subtree")
|
||||||
|
}
|
||||||
|
srccursor := b.Left(from)
|
||||||
|
dstcursor := b.Left(to)
|
||||||
|
for srccursor < b.lastNode && srccursor < len(*b.Binary) {
|
||||||
|
srcstart, srcend := b.LevelRange(srccursor, from)
|
||||||
|
dststart, dstend := b.LevelRange(dstcursor, to)
|
||||||
|
slog.Info("MoveSubTree()", "srcstart", srcstart, "srcend", srcend, "dststart", dststart, "dstend", dstend, "source", (*b.Binary)[srcstart + BSTDATAOFFSET:srcend + BSTDATAOFFSET + 1], "dest", (*b.Binary)[dststart + BSTDATAOFFSET:dstend + BSTDATAOFFSET + 1])
|
||||||
|
copy((*b.Binary)[dststart + BSTDATAOFFSET:dstend + BSTDATAOFFSET + 1], (*b.Binary)[srcstart + BSTDATAOFFSET:srcend + BSTDATAOFFSET + 1])
|
||||||
|
b.SetEmptyRange(srcstart, srcend)
|
||||||
|
|
||||||
|
srccursor = b.Left(srccursor)
|
||||||
|
dstcursor = b.Left(dstcursor)
|
||||||
|
slog.Info("MoveSubTree() post-copy", "srcstart", srcstart, "srcend", srcend, "dststart", dststart, "dstend", dstend, "data", b.Binary)
|
||||||
|
}
|
||||||
|
b.Copy(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) IsLeaf(index int) bool {
|
||||||
|
childIdx := 2 * (index)
|
||||||
|
return (*b)[childIdx + LEFTOFFSET + BSTDATAOFFSET] == (*b)[childIdx + RIGHTOFFSET + BSTDATAOFFSET]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) IsLeaf(index int) bool {
|
||||||
|
childIdx := 2 * (index)
|
||||||
|
leftIdx := childIdx + LEFTOFFSET
|
||||||
|
rightIdx := childIdx + RIGHTOFFSET
|
||||||
|
return b.Get(leftIdx) == b.Get(rightIdx) && leftIdx != b.zeroIndex && rightIdx != b.zeroIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) ParentIndex(index int) (parent int) {
|
||||||
|
parent = index
|
||||||
|
if b.IsRight(index) {
|
||||||
|
parent -= RIGHTOFFSET
|
||||||
|
} else {
|
||||||
|
parent -= LEFTOFFSET
|
||||||
|
}
|
||||||
|
parent = parent / 2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Depth(index int) int {
|
||||||
|
return int(math.Ceil(math.Log2(float64(index + 2))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Width(index int) int {
|
||||||
|
return int(math.Exp2(float64(b.Depth(index) - 1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) IsFullLevel(index int) bool {
|
||||||
|
width := b.Width(index)
|
||||||
|
start := width - 1
|
||||||
|
for _, v := range (*b)[start + BSTDATAOFFSET: start + width + BSTDATAOFFSET] {
|
||||||
|
if v == b.Empty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) IsEmpty(index int) bool {
|
||||||
|
return (*b)[index + BSTDATAOFFSET] == b.Empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Ascend(idx int, item Key) int {
|
||||||
|
if item < b.Get(idx) {
|
||||||
|
return b.Left(idx)
|
||||||
|
}
|
||||||
|
return b.Right(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binary[Key]) Grow(index int) {
|
||||||
|
size := len(*b) - BSTDATAOFFSET
|
||||||
|
slog.Info("Grow()", "size", size, "index", index, "cap", cap(*b))
|
||||||
|
if index >= size / 2 {
|
||||||
|
targetSize := (size + (2 * index + 1) * 3) + BSTDATAOFFSET
|
||||||
|
if cap(*b) < targetSize {
|
||||||
|
expandArray := make(Binary[Key], ((targetSize - cap(*b)) * 2) + BSTDATAOFFSET)
|
||||||
|
*b = append(*b, expandArray...)
|
||||||
|
slog.Info("Grow()", "size", size, "index", index, "targetsize", targetSize, "cap", cap(*b), "new", cap(expandArray))
|
||||||
|
}
|
||||||
|
*b = (*b)[:targetSize]
|
||||||
|
}
|
||||||
|
slog.Info("Grow()", "size", len(*b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) RotateRight(index, right, left int) int {
|
||||||
|
slog.Info("PreRotateRight", "index", index, "leftindex", left, "rightindex", right, "left", b.Get(left), "node", b.Get(index), "right", b.Get(right))
|
||||||
|
b.Copy(index, right)
|
||||||
|
b.Copy(left, index)
|
||||||
|
b.SetEmpty(left)
|
||||||
|
slog.Info("RotateRight", "index", index, "leftindex", left, "rightindex", right, "left", b.Get(left), "node", b.Get(index), "right", b.Get(right))
|
||||||
|
return left
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) RotateLeft(index, left, right int) int {
|
||||||
|
slog.Info("PreRotateLeft", "index", index, "leftindex", left, "rightindex", right, "left", b.Get(left), "node", b.Get(index), "right", b.Get(right))
|
||||||
|
b.Copy(index, left)
|
||||||
|
b.Copy(right, index)
|
||||||
|
b.SetEmpty(right)
|
||||||
|
slog.Info("RotateLeft", "index", index, "leftindex", left, "rightindex", right, "left", b.Get(left), "node", b.Get(index), "right", b.Get(right))
|
||||||
|
return right
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
func (b *Binary[Key]) Insert(item Key) (idx int) {
|
||||||
|
idx = BSTROOTNODEID
|
||||||
|
|
||||||
|
slog.Info("Insert()", "item", item)
|
||||||
|
for {
|
||||||
|
b.Grow(idx)
|
||||||
|
|
||||||
|
if b.IsEmpty(idx) {
|
||||||
|
b.Set(idx, item)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
rightIdx := b.Right(idx)
|
||||||
|
leftIdx := b.Left(idx)
|
||||||
|
if b.IsInternal(idx) {
|
||||||
|
|
||||||
|
idx = b.Ascend(idx, item)
|
||||||
|
} else if b.IsLeaf(idx) {
|
||||||
|
|
||||||
|
idx = b.Ascend(idx, item)
|
||||||
|
} else {
|
||||||
|
slog.Info("Insert()", "item", item, "index", idx)
|
||||||
|
if item < b.Get(idx) {
|
||||||
|
if ! b.IsEmpty(rightIdx) {
|
||||||
|
idx = leftIdx
|
||||||
|
} else {
|
||||||
|
if ! b.IsLeaf(leftIdx) {
|
||||||
|
panic("the left node is not a leaf")
|
||||||
|
}
|
||||||
|
idx = b.RotateRight(idx, rightIdx, leftIdx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ! b.IsEmpty(leftIdx) {
|
||||||
|
idx = rightIdx
|
||||||
|
} else {
|
||||||
|
if ! b.IsLeaf(rightIdx) {
|
||||||
|
panic("the right node is not a leaf")
|
||||||
|
}
|
||||||
|
idx = b.RotateLeft(idx, leftIdx, rightIdx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (b *BinaryTree[Key]) Insert(item Key) (idx int) {
|
||||||
|
idx = BSTROOTNODEID
|
||||||
|
slog.Info("Insert()", "item", item)
|
||||||
|
|
||||||
|
for {
|
||||||
|
b.Grow(idx)
|
||||||
|
|
||||||
|
if b.IsEmpty(idx) {
|
||||||
|
b.Set(idx, item)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
rightIdx := b.Right(idx)
|
||||||
|
leftIdx := b.Left(idx)
|
||||||
|
if b.IsInternal(idx) {
|
||||||
|
idx = b.Ascend(idx, item)
|
||||||
|
} else if b.IsLeaf(idx) {// && b.IsFullLevel(idx) {
|
||||||
|
/*
|
||||||
|
if ! b.IsFullLevel(idx) {
|
||||||
|
parent := b.ParentIndex(idx)
|
||||||
|
if b.IsRight(parent) {
|
||||||
|
idx = b.RotateLeft(parent, b.Left(parent), b.Right(parent))
|
||||||
|
} else {
|
||||||
|
idx = b.RotateRight(parent, b.Right(parent), b.Left(parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
idx = b.Ascend(idx, item)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
slog.Info("Insert()", "item", item, "index", idx)
|
||||||
|
if item < b.Get(idx) { // left
|
||||||
|
slog.Info("Insert()", "item", item, "index", idx)
|
||||||
|
if ! b.IsEmpty(rightIdx) {
|
||||||
|
idx = leftIdx
|
||||||
|
} else {
|
||||||
|
if ! b.IsLeaf(leftIdx) {
|
||||||
|
panic("the left node is not a leaf")
|
||||||
|
}
|
||||||
|
idx = b.RotateRight(idx, rightIdx, leftIdx)
|
||||||
|
}
|
||||||
|
} else { // right
|
||||||
|
if ! b.IsEmpty(leftIdx) {
|
||||||
|
idx = rightIdx
|
||||||
|
} else {
|
||||||
|
if ! b.IsLeaf(rightIdx) {
|
||||||
|
idx = rightIdx
|
||||||
|
//panic("the right node is not a leaf")
|
||||||
|
} else {
|
||||||
|
idx = b.RotateLeft(idx, leftIdx, rightIdx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BinaryBFSItem[Key cmp.Ordered] func(index int, depth int, v Key)
|
||||||
|
|
||||||
|
func (b *Binary[Key]) BFS(c BinaryBFSItem[Key]) (depth int) {
|
||||||
|
queue := []int{ BSTROOTNODEID }
|
||||||
|
for len(queue) > 0 {
|
||||||
|
x := queue[0]
|
||||||
|
queue = queue[1:]
|
||||||
|
depth = b.Depth(x)
|
||||||
|
c(x, depth, b.Get(x))
|
||||||
|
leftIdx := b.Left(x)
|
||||||
|
rightIdx := b.Right(x)
|
||||||
|
if ! b.IsEmpty(leftIdx) {
|
||||||
|
queue = append(queue, leftIdx)
|
||||||
|
}
|
||||||
|
if ! b.IsEmpty(rightIdx) {
|
||||||
|
queue = append(queue, b.Right(x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
117
internal/search/binary_test.go
Normal file
117
internal/search/binary_test.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
|
package search
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewBinary(t *testing.T) {
|
||||||
|
b := NewBinaryTree[int](9)
|
||||||
|
assert.NotNil(t, b)
|
||||||
|
|
||||||
|
for _, v := range []struct { expectedindex , value int } {
|
||||||
|
{ expectedindex: BSTROOTNODEID, value: 7 },
|
||||||
|
{ expectedindex: 2*BSTROOTNODEID + LEFTOFFSET, value: 4 },
|
||||||
|
{ expectedindex: 2*BSTROOTNODEID + RIGHTOFFSET, value: 9 },
|
||||||
|
{ expectedindex: 4 + RIGHTOFFSET, value: 18 },
|
||||||
|
{ expectedindex: 2 + RIGHTOFFSET, value: 5 },
|
||||||
|
{ expectedindex: 4 + RIGHTOFFSET, value: 20 },
|
||||||
|
{ expectedindex: 4 - LEFTOFFSET, value: 3 },
|
||||||
|
{ expectedindex: 8 - LEFTOFFSET, value: 2 },
|
||||||
|
{ expectedindex: 8 - LEFTOFFSET, value: 1 },
|
||||||
|
{ expectedindex: 16 - LEFTOFFSET, value: 0 },
|
||||||
|
{ expectedindex: 16 - LEFTOFFSET, value: -1 },
|
||||||
|
} {
|
||||||
|
idx := b.Insert(v.value)
|
||||||
|
slog.Info("TestInsert()", "index", idx, "value", v, "b", b, "data", b.Binary)
|
||||||
|
assert.Equal(t, v.expectedindex, idx)
|
||||||
|
assert.Equal(t, v.value, b.Get(v.expectedindex))
|
||||||
|
}
|
||||||
|
assert.Equal(t, 7, b.zeroIndex)
|
||||||
|
b.BFS(func(index int, depth int, v int) {
|
||||||
|
slog.Info("BFS()", "index", index, "depth", depth, "value", v, "b", b, "data", b.Binary)
|
||||||
|
assert.Equal(t, v, b.Get(index))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinaryDepth(t *testing.T) {
|
||||||
|
b := NewBinaryTree[int](9)
|
||||||
|
assert.NotNil(t, b)
|
||||||
|
for _, v := range []struct{ expecteddepth, index int }{
|
||||||
|
{ expecteddepth: 1, index: 0 },
|
||||||
|
{ expecteddepth: 2, index: 1 },
|
||||||
|
{ expecteddepth: 2, index: 2 },
|
||||||
|
{ expecteddepth: 3, index: 5 },
|
||||||
|
} {
|
||||||
|
assert.Equal(t, v.expecteddepth, b.Depth(v.index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinaryWidth(t *testing.T) {
|
||||||
|
b := NewBinaryTree[int](9)
|
||||||
|
assert.NotNil(t, b)
|
||||||
|
for _, v := range []struct{ expectedwidth, index int }{
|
||||||
|
{ expectedwidth: 1, index: 0 },
|
||||||
|
{ expectedwidth: 2, index: 1 },
|
||||||
|
{ expectedwidth: 2, index: 2 },
|
||||||
|
{ expectedwidth: 4, index: 5 },
|
||||||
|
{ expectedwidth: 8, index: 8 },
|
||||||
|
{ expectedwidth: 16, index: 17 },
|
||||||
|
} {
|
||||||
|
assert.Equal(t, v.expectedwidth, b.Width(v.index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinaryInSubTree(t *testing.T) {
|
||||||
|
b := NewBinaryTree[int](9)
|
||||||
|
assert.NotNil(t, b)
|
||||||
|
for _, v := range []struct{ expected bool; tree, index, start, end int }{
|
||||||
|
{ expected: true, tree: 0, index: 0, start: 0, end: 0 },
|
||||||
|
{ expected: true, tree: 4, index: 19, start: 19, end: 22 },
|
||||||
|
{ expected: false, tree: 4, index: 13, start: -1, end: -1 },
|
||||||
|
{ expected: false, tree: 5, index: 6, start: -1, end: -1 },
|
||||||
|
} {
|
||||||
|
start, end := b.LevelRange(v.index, v.tree)
|
||||||
|
assert.Equal(t, v.start, start)
|
||||||
|
assert.Equal(t, v.end, end)
|
||||||
|
assert.Equal(t, v.expected, b.InSubTree(v.index, v.tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinaryMoveSubTree(t *testing.T) {
|
||||||
|
expected := []int{ 0, 4, 0, 7, 0, 0, 5, 18, 0, 0, 0, 0, 0, 0, 9, 20 }
|
||||||
|
b := NewBinaryTree[int](9)
|
||||||
|
assert.NotNil(t, b)
|
||||||
|
|
||||||
|
b.Insert(7)
|
||||||
|
b.Insert(4)
|
||||||
|
b.Insert(9)
|
||||||
|
b.Insert(18)
|
||||||
|
b.Insert(20)
|
||||||
|
b.MoveSubTree(2, 6)
|
||||||
|
b.RotateRight(0, 2, 1)
|
||||||
|
b.Insert(5)
|
||||||
|
assert.EqualValues(t, expected, (*b.Binary)[:16])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinaryMaxDepth(t *testing.T) {
|
||||||
|
//expected := []int{ 0, 4, 0, 7, 0, 0, 5, 18, 0, 0, 0, 0, 0, 0, 9, 20 }
|
||||||
|
b := NewBinaryTree[int](9)
|
||||||
|
assert.NotNil(t, b)
|
||||||
|
|
||||||
|
b.Insert(7)
|
||||||
|
b.Insert(4)
|
||||||
|
b.Insert(9)
|
||||||
|
b.Insert(18)
|
||||||
|
b.Insert(20)
|
||||||
|
b.Insert(5)
|
||||||
|
b.Insert(11)
|
||||||
|
b.Insert(-1)
|
||||||
|
b.Insert(33)
|
||||||
|
b.Insert(15)
|
||||||
|
b.Insert(13)
|
||||||
|
slog.Info("TestBinaryMaxDepth", "maxdepth", b.MaxDepth(), "last", b.LastNode(), "data", (*b.Binary))
|
||||||
|
}
|
93
internal/search/trie.go
Normal file
93
internal/search/trie.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
|
package search
|
||||||
|
|
||||||
|
import (
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
AlphabetSize uint = 26
|
||||||
|
FirstAlphabetChar = uint8('a')
|
||||||
|
)
|
||||||
|
|
||||||
|
type String string
|
||||||
|
|
||||||
|
type TrieNode struct {
|
||||||
|
Children []*TrieNode
|
||||||
|
Words int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTrieNode() *TrieNode {
|
||||||
|
return &TrieNode{ Children: make([]*TrieNode, AlphabetSize), Words: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TrieNode) Insert(key string) {
|
||||||
|
current := r
|
||||||
|
for _, c := range key {
|
||||||
|
charIndex := uint8(c) - FirstAlphabetChar
|
||||||
|
if current.Children[charIndex] == nil {
|
||||||
|
current.Children[charIndex] = NewTrieNode()
|
||||||
|
}
|
||||||
|
current = current.Children[charIndex]
|
||||||
|
}
|
||||||
|
current.Words++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s String) Index(root *TrieNode) {
|
||||||
|
root.Insert(string(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TrieNode) Search(key string) bool {
|
||||||
|
current := r
|
||||||
|
for _, c := range key {
|
||||||
|
charIndex := uint8(c) - FirstAlphabetChar
|
||||||
|
if current.Children[charIndex] == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
current = current.Children[charIndex]
|
||||||
|
}
|
||||||
|
return (current.Words > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TrieNode) Delete(word string) bool {
|
||||||
|
current := r
|
||||||
|
var last *TrieNode = nil
|
||||||
|
var lastChar rune
|
||||||
|
for _, c := range word {
|
||||||
|
charIndex := uint8(c) - FirstAlphabetChar
|
||||||
|
if current.Children[charIndex] == nil {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
count := 0
|
||||||
|
for i := uint(0); i < AlphabetSize; i++ {
|
||||||
|
if current.Children[i] != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count > 1 {
|
||||||
|
last = current
|
||||||
|
lastChar = c
|
||||||
|
}
|
||||||
|
current = current.Children[charIndex]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
for i := uint(0); i < AlphabetSize; i++ {
|
||||||
|
if current.Children[i] != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) { // is prefix
|
||||||
|
current.Words--
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if last != nil { // shares a common prefix with other words
|
||||||
|
last.Children[lastChar] = nil
|
||||||
|
return true
|
||||||
|
} else { // does not share a common prefix
|
||||||
|
r.Children[word[0]] = nil
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
16
internal/search/trie_test.go
Normal file
16
internal/search/trie_test.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
|
package search
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
_ "log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewTrie(t *testing.T) {
|
||||||
|
n := NewTrieNode()
|
||||||
|
assert.NotNil(t, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user