diff --git a/datastructure/node.go b/datastructure/node.go index 1ceb2b3..ae662ee 100644 --- a/datastructure/node.go +++ b/datastructure/node.go @@ -33,3 +33,15 @@ type QueueNode[T any] struct { func NewQueueNode[T any](value T) *QueueNode[T] { 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} +} diff --git a/datastructure/tree/bstree.go b/datastructure/tree/bstree.go new file mode 100644 index 0000000..fcd09f7 --- /dev/null +++ b/datastructure/tree/bstree.go @@ -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) +} diff --git a/datastructure/tree/bstree_test.go b/datastructure/tree/bstree_test.go new file mode 100644 index 0000000..346b61c --- /dev/null +++ b/datastructure/tree/bstree_test.go @@ -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() +} diff --git a/datastructure/tree/tree_internal.go b/datastructure/tree/tree_internal.go new file mode 100644 index 0000000..1a7b956 --- /dev/null +++ b/datastructure/tree/tree_internal.go @@ -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 +}