2024-08-29 02:40:17 +00:00
|
|
|
// 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)
|
2024-09-19 08:17:00 +00:00
|
|
|
bst = &BinaryTree[Key]{ Binary: &tree, least: 0, greatest: 0, lastNode: 0 }
|
2024-08-29 02:40:17 +00:00
|
|
|
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
|
|
|
|
}
|