main.go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
package main

import (
	"fmt"
	"sync"
)

// Ordered is a minimal ordered constraint (no extra deps).
// It allows using < and > on the type parameter.
type Ordered interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64 |
		~string
}

// BST is a thread-safe Binary Search Tree supporting duplicates.
type BST[T Ordered] struct {
	mu   sync.RWMutex
	root *node[T]
}

type node[T Ordered] struct {
	value T
	count int
	left  *node[T]
	right *node[T]
}

func NewBST[T Ordered]() *BST[T] { return &BST[T]{} }

func (b *BST[T]) Insert(value T) {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.root = insertNode(b.root, value)
}

func insertNode[T Ordered](n *node[T], value T) *node[T] {
	if n == nil {
		return &node[T]{value: value, count: 1}
	}
	if value < n.value {
		n.left = insertNode(n.left, value)
		return n
	}
	if value > n.value {
		n.right = insertNode(n.right, value)
		return n
	}
	n.count++
	return n
}

func (b *BST[T]) Search(value T) bool {
	b.mu.RLock()
	defer b.mu.RUnlock()
	cur := b.root
	for cur != nil {
		if value < cur.value {
			cur = cur.left
			continue
		}
		if value > cur.value {
			cur = cur.right
			continue
		}
		return true
	}
	return false
}

// Delete removes a single occurrence of value. Returns true if something was removed.
func (b *BST[T]) Delete(value T) bool {
	b.mu.Lock()
	defer b.mu.Unlock()
	var deleted bool
	b.root, deleted = deleteNode(b.root, value)
	return deleted
}

func deleteNode[T Ordered](n *node[T], value T) (*node[T], bool) {
	if n == nil {
		return nil, false
	}
	if value < n.value {
		var deleted bool
		n.left, deleted = deleteNode(n.left, value)
		return n, deleted
	}
	if value > n.value {
		var deleted bool
		n.right, deleted = deleteNode(n.right, value)
		return n, deleted
	}

	// value == n.value
	if n.count > 1 {
		n.count--
		return n, true
	}

	// Node with 0 or 1 child
	if n.left == nil {
		return n.right, true
	}
	if n.right == nil {
		return n.left, true
	}

	// Node with 2 children: replace with inorder successor (min of right subtree)
	succ := n.right
	for succ.left != nil {
		succ = succ.left
	}
	n.value = succ.value
	n.count = succ.count
	// Delete successor node entirely (all its duplicates moved here)
	n.right = deleteAll(n.right, succ.value)
	return n, true
}

func deleteAll[T Ordered](n *node[T], value T) *node[T] {
	if n == nil {
		return nil
	}
	if value < n.value {
		n.left = deleteAll(n.left, value)
		return n
	}
	if value > n.value {
		n.right = deleteAll(n.right, value)
		return n
	}
	// remove this node completely
	if n.left == nil {
		return n.right
	}
	if n.right == nil {
		return n.left
	}
	succ := n.right
	for succ.left != nil {
		succ = succ.left
	}
	n.value = succ.value
	n.count = succ.count
	n.right = deleteAll(n.right, succ.value)
	return n
}

func (b *BST[T]) InOrder() []T {
	b.mu.RLock()
	defer b.mu.RUnlock()
	var out []T
	inOrder(b.root, &out)
	return out
}

func inOrder[T Ordered](n *node[T], out *[]T) {
	if n == nil {
		return
	}
	inOrder(n.left, out)
	for i := 0; i < n.count; i++ {
		*out = append(*out, n.value)
	}
	inOrder(n.right, out)
}

func (b *BST[T]) PreOrder() []T {
	b.mu.RLock()
	defer b.mu.RUnlock()
	var out []T
	preOrder(b.root, &out)
	return out
}

func preOrder[T Ordered](n *node[T], out *[]T) {
	if n == nil {
		return
	}
	for i := 0; i < n.count; i++ {
		*out = append(*out, n.value)
	}
	preOrder(n.left, out)
	preOrder(n.right, out)
}

func (b *BST[T]) PostOrder() []T {
	b.mu.RLock()
	defer b.mu.RUnlock()
	var out []T
	postOrder(b.root, &out)
	return out
}

func postOrder[T Ordered](n *node[T], out *[]T) {
	if n == nil {
		return
	}
	postOrder(n.left, out)
	postOrder(n.right, out)
	for i := 0; i < n.count; i++ {
		*out = append(*out, n.value)
	}
}

// Height returns the tree height (empty tree = 0).
func (b *BST[T]) Height() int {
	b.mu.RLock()
	defer b.mu.RUnlock()
	return height(b.root)
}

func height[T Ordered](n *node[T]) int {
	if n == nil {
		return 0
	}
	lh := height(n.left)
	rh := height(n.right)
	if lh > rh {
		return lh + 1
	}
	return rh + 1
}

func main() {
	bst := NewBST[int]()
	for _, v := range []int{5, 3, 7, 1, 4, 6, 8, 7, 7} {
		bst.Insert(v)
	}

	fmt.Println("search 4:", bst.Search(4))
	fmt.Println("height:", bst.Height())
	fmt.Println("in-order:", bst.InOrder())
	fmt.Println("pre-order:", bst.PreOrder())
	fmt.Println("post-order:", bst.PostOrder())

	bst.Delete(7)
	fmt.Println("after delete one 7:", bst.InOrder())
	bst.Delete(7)
	bst.Delete(7)
	fmt.Println("after delete all 7s:", bst.InOrder())
}

How It Works

Generic binary search tree with thread-safe insert, search, delete, and traversal helpers.

Uses a RWMutex to guard the root, inserts recursively respecting ordering, supports find and delete with child rewiring, and exposes traversal functions for in-order, pre-order, and post-order plus height calculation.

Key Concepts

  • 1Mutex guards concurrent mutations on the tree.
  • 2Delete handles leaf, single-child, and two-child replacement cases.
  • 3Traversal helpers return sorted output or structural insights like height.

When to Use This Pattern

  • In-memory indexes for ordered data.
  • Teaching tree algorithms with concurrency considerations.
  • Building foundational data structures for interview practice.

Best Practices

  • Prefer balanced trees for large datasets to avoid skew.
  • Hold locks only as long as needed to reduce contention.
  • Validate comparator assumptions when using generics.
Go Version1.18
Difficultyintermediate
Production ReadyYes
Lines of Code245