jx/internal/search/binary.go
Matthew Rich 8910fced57
Some checks failed
Lint / golangci-lint (push) Has been cancelled
Declarative Tests / test (push) Has been cancelled
Declarative Tests / build-fedora (push) Has been cancelled
Declarative Tests / build-ubuntu-focal (push) Has been cancelled
add bst
2024-09-19 08:17:00 +00:00

405 lines
9.7 KiB
Go

// 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, least: 0, greatest: 0, lastNode: 0 }
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
}