Binary Search Tree

A simple implementation of binary search tree data structure using Go Generics

Binary Search Tree
package main

import (
	"fmt"

	"golang.org/x/exp/constraints"
)

type Node[T constraints.Ordered] struct {
	key        T
	leftChild  *Node[T]
	rightChild *Node[T]
	parent     *Node[T]
}

func (n *Node[T]) HasLeft() bool   { return n.leftChild != nil }
func (n *Node[T]) HasRight() bool  { return n.rightChild != nil }
func (n *Node[T]) HasParent() bool { return n.parent != nil }

func (n *Node[T]) Delete() {

	if n.parent == nil {
		return
	}

	if !n.HasLeft() && !n.HasRight() {
		if n.parent.leftChild == n {
			n.parent.leftChild = nil
		} else {
			n.parent.rightChild = nil
		}

	} else if n.HasLeft() && !n.HasRight() {
		n.leftChild.Transplant(n)

	} else if !n.HasLeft() && n.HasRight() {
		n.rightChild.Transplant(n)

	} else {

		// has both childrens
		if n.rightChild.leftChild == nil {
			n.leftChild.parent = n.rightChild
			n.rightChild.leftChild = n.leftChild
			n.rightChild.Transplant(n)
			return
		}

		minNode := n.rightChild.Min()
		minCopy := *minNode
		minNode.Delete()

		minCopy.leftChild = n.leftChild
		minCopy.rightChild = n.rightChild

		n.leftChild.parent = &minCopy
		n.rightChild.parent = &minCopy

		if n.parent.leftChild == n {
			n.parent.leftChild = &minCopy
		} else {
			n.parent.rightChild = &minCopy
		}

	}
}

func (n *Node[T]) Transplant(original *Node[T]) {

	if original == nil {
		return
	}

	n.parent = original.parent

	if original.HasParent() {
		if original.parent.leftChild == original {
			n.parent.leftChild = n
		} else {
			n.parent.rightChild = n
		}
	}
	*original = *n
}

func (n *Node[T]) Min() *Node[T] {
	if n.HasLeft() {
		return n.leftChild.Min()
	}
	return n
}

func (n *Node[T]) Max() *Node[T] {
	if n.HasRight() {
		return n.rightChild.Max()
	}
	return n
}

func (n *Node[T]) Search(value T) *Node[T] {
	if n.key == value {
		return n
	} else if n.key < value {
		if n.HasRight() {
			return n.rightChild.Search(value)
		} else {
			return nil
		}
	} else {
		if n.HasLeft() {
			return n.leftChild.Search(value)
		} else {
			return nil
		}
	}
}

func (n *Node[T]) Insert(node *Node[T]) {
	if n.key < node.key {
		if n.HasRight() {
			n.rightChild.Insert(node)
		} else {
			n.rightChild = node
			node.parent = n
		}
	} else {
		if n.HasLeft() {
			n.leftChild.Insert(node)
		} else {
			n.leftChild = node
			node.parent = n
		}
	}
}

func (n *Node[T]) InOrderWalk(root string) string {

	if n.HasLeft() {
		root = n.leftChild.InOrderWalk(root)
	}
	root = fmt.Sprintf("%s %v", root, n.key)
	if n.HasRight() {
		root = n.rightChild.InOrderWalk(root)
	}
	return root
}

func (n *Node[T]) traversePreOrder(root string, padding string, pointer string, hasRightSibling bool) string {

	str := fmt.Sprintf("%s%s%s%v\n", root, padding, pointer, n.key)

	if !n.HasRight() && !n.HasLeft() {
		return str
	}

	var paddingForBoth string
	if paddingForBoth = fmt.Sprintf("%s%s", padding, "   "); hasRightSibling {
		paddingForBoth = fmt.Sprintf("%s%s", padding, "│  ")
	}

	pointerForRight := "└──"
	var pointerForLeft string
	if pointerForLeft = "├──"; !n.HasRight() {
		pointerForLeft = "└──"
	}

	if n.HasLeft() {
		str = n.leftChild.traversePreOrder(str, paddingForBoth, pointerForLeft, n.HasRight())
	} else {
		str = fmt.Sprintf("%s%s%s%v\n", str, paddingForBoth, pointerForLeft, "*")
	}
	if n.HasRight() {
		str = n.rightChild.traversePreOrder(str, paddingForBoth, pointerForRight, false)
	} else {
		str = fmt.Sprintf("%s%s%s%v\n", str, paddingForBoth, pointerForRight, "*")
	}

	return str
}

type BST[T constraints.Ordered] struct {
	root *Node[T]
}

func NewBST[T constraints.Ordered](value T) *BST[T] {
	return &BST[T]{root: &Node[T]{key: value}}
}

func (bst *BST[T]) Insert(value T) {

	newNode := &Node[T]{key: value}
	if bst.root == nil {
		bst.root = newNode
		return
	}
	bst.root.Insert(newNode)
}

func (bst *BST[T]) InOrderTreeWalk() {
	fmt.Println(bst.root.InOrderWalk(""))
}

func (bst *BST[T]) Search(value T) {
	if r := bst.root.Search(value); r != nil {
		fmt.Println(r.traversePreOrder("", "", "", false))
	} else {
		fmt.Println("element not found")
	}
}

func (bst *BST[T]) Print() {
	fmt.Println(bst.root.traversePreOrder("", "", "", false))
}

func (bst *BST[T]) Min() {
	fmt.Printf("min of the BST is: %v\n", bst.root.Min().key)
}

func (bst *BST[T]) Max() {
	fmt.Printf("max of the BST is: %v\n", bst.root.Max().key)
}

func (bst *BST[T]) Delete(value T) {
	if node := bst.root.Search(value); node != nil {
		node.Delete()
	}
}

func main() {

	tree := NewBST(10)

	tree.Insert(5)
	tree.Insert(8)
	tree.Insert(12)
	tree.Insert(2)
	tree.Insert(3)
	tree.Insert(45)
	tree.Insert(11)
	tree.Insert(20)

	tree.Print()
	tree.InOrderTreeWalk()
	tree.Min()
	tree.Max()

	tree.Search(12)
	tree.Search(13)

	tree.Delete(12)
	tree.InOrderTreeWalk()
	tree.Print()

	tree.Delete(5)
	tree.InOrderTreeWalk()
	tree.Print()
}