mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-14 17:52:28 +08:00
feat: add BSTree which is binary search tree
This commit is contained in:
@@ -33,3 +33,15 @@ type QueueNode[T any] struct {
|
|||||||
func NewQueueNode[T any](value T) *QueueNode[T] {
|
func NewQueueNode[T any](value T) *QueueNode[T] {
|
||||||
return &QueueNode[T]{value, nil}
|
return &QueueNode[T]{value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TreeNode is node of tree
|
||||||
|
type TreeNode[T any] struct {
|
||||||
|
Data T
|
||||||
|
Left *TreeNode[T]
|
||||||
|
Right *TreeNode[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTreeNode return a TreeNode pointer
|
||||||
|
func NewTreeNode[T any](data T) *TreeNode[T] {
|
||||||
|
return &TreeNode[T]{data, nil, nil}
|
||||||
|
}
|
||||||
|
|||||||
51
datastructure/tree/bstree.go
Normal file
51
datastructure/tree/bstree.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package datastructure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/datastructure"
|
||||||
|
"github.com/duke-git/lancet/lancetconstraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BSTree is a binary search tree data structure in which each node has at most two children,
|
||||||
|
// which are referred to as the left child and the right child.
|
||||||
|
// In BSTree: leftNode < rootNode < rightNode
|
||||||
|
// type T should implments Compare function of lancetconstraints.Comparator interface
|
||||||
|
type BSTree[T any] struct {
|
||||||
|
root *datastructure.TreeNode[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBSTree create a BSTree pointer
|
||||||
|
func NewBSTree[T any](rootData T) *BSTree[T] {
|
||||||
|
root := datastructure.NewTreeNode(rootData)
|
||||||
|
return &BSTree[T]{root}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert data into BSTree
|
||||||
|
func (t *BSTree[T]) Insert(data T, comparator lancetconstraints.Comparator) {
|
||||||
|
root := t.root
|
||||||
|
newNode := datastructure.NewTreeNode(data)
|
||||||
|
if root == nil {
|
||||||
|
t.root = newNode
|
||||||
|
} else {
|
||||||
|
insertTreeNode(root, newNode, comparator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeLevel get node level in BSTree
|
||||||
|
func (t *BSTree[T]) NodeLevel(node *datastructure.TreeNode[T]) int {
|
||||||
|
if node == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
left := float64(t.NodeLevel(node.Left))
|
||||||
|
right := float64(t.NodeLevel(node.Right))
|
||||||
|
|
||||||
|
return int(math.Max(left, right)) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the bstree structure
|
||||||
|
func (t *BSTree[T]) Print() {
|
||||||
|
maxLevel := t.NodeLevel(t.root)
|
||||||
|
nodes := []*datastructure.TreeNode[T]{t.root}
|
||||||
|
printTreeNodes(nodes, 1, maxLevel)
|
||||||
|
}
|
||||||
29
datastructure/tree/bstree_test.go
Normal file
29
datastructure/tree/bstree_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package datastructure
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
type intComparator struct{}
|
||||||
|
|
||||||
|
func (c *intComparator) Compare(v1, v2 any) int {
|
||||||
|
val1, _ := v1.(int)
|
||||||
|
val2, _ := v2.(int)
|
||||||
|
|
||||||
|
if val1 < val2 {
|
||||||
|
return -1
|
||||||
|
} else if val1 > val2 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBSTree_Insert(t *testing.T) {
|
||||||
|
bstree := NewBSTree(6)
|
||||||
|
|
||||||
|
comparator := &intComparator{}
|
||||||
|
bstree.Insert(7, comparator)
|
||||||
|
bstree.Insert(5, comparator)
|
||||||
|
bstree.Insert(2, comparator)
|
||||||
|
bstree.Insert(4, comparator)
|
||||||
|
|
||||||
|
bstree.Print()
|
||||||
|
}
|
||||||
99
datastructure/tree/tree_internal.go
Normal file
99
datastructure/tree/tree_internal.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package datastructure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/datastructure"
|
||||||
|
"github.com/duke-git/lancet/lancetconstraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
func insertTreeNode[T any](rootNode, newNode *datastructure.TreeNode[T], comparator lancetconstraints.Comparator) {
|
||||||
|
if comparator.Compare(newNode.Data, rootNode.Data) == -1 {
|
||||||
|
if rootNode.Left == nil {
|
||||||
|
rootNode.Left = newNode
|
||||||
|
} else {
|
||||||
|
insertTreeNode(rootNode.Left, newNode, comparator)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if rootNode.Right == nil {
|
||||||
|
rootNode.Right = newNode
|
||||||
|
} else {
|
||||||
|
insertTreeNode(rootNode.Right, newNode, comparator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTreeNodes[T any](nodes []*datastructure.TreeNode[T], level, maxLevel int) {
|
||||||
|
if len(nodes) == 0 || isAllNil(nodes) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
floor := maxLevel - level
|
||||||
|
endgeLines := int(math.Pow(float64(2), (math.Max(float64(floor)-1, 0))))
|
||||||
|
firstSpaces := int(math.Pow(float64(2), float64(floor))) - 1
|
||||||
|
betweenSpaces := int(math.Pow(float64(2), float64(floor)+1)) - 1
|
||||||
|
|
||||||
|
printSpaces(firstSpaces)
|
||||||
|
|
||||||
|
newNodes := []*datastructure.TreeNode[T]{}
|
||||||
|
for _, node := range nodes {
|
||||||
|
if node != nil {
|
||||||
|
fmt.Printf("%v", node.Data)
|
||||||
|
newNodes = append(newNodes, node.Left)
|
||||||
|
newNodes = append(newNodes, node.Right)
|
||||||
|
} else {
|
||||||
|
newNodes = append(newNodes, nil)
|
||||||
|
newNodes = append(newNodes, nil)
|
||||||
|
printSpaces(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
printSpaces(betweenSpaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("")
|
||||||
|
|
||||||
|
for i := 1; i <= endgeLines; i++ {
|
||||||
|
for j := 0; j < len(nodes); j++ {
|
||||||
|
printSpaces(firstSpaces - i)
|
||||||
|
if nodes[j] == nil {
|
||||||
|
printSpaces(endgeLines + endgeLines + i + 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if nodes[j].Left != nil {
|
||||||
|
fmt.Print("/")
|
||||||
|
} else {
|
||||||
|
printSpaces(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
printSpaces(i + i - 1)
|
||||||
|
|
||||||
|
if nodes[j].Right != nil {
|
||||||
|
fmt.Print("\\")
|
||||||
|
} else {
|
||||||
|
printSpaces(1)
|
||||||
|
}
|
||||||
|
printSpaces(endgeLines + endgeLines - 1)
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
printTreeNodes(newNodes, level+1, maxLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// printSpaces
|
||||||
|
func printSpaces(n int) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
fmt.Print(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAllNil[T any](nodes []*datastructure.TreeNode[T]) bool {
|
||||||
|
for _, v := range nodes {
|
||||||
|
if v != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user