added test fot AvlTree and implemented inorder, postorder traversal
Go / build (push) Successful in 34s

This commit is contained in:
Acid
2026-07-27 22:17:45 -04:00
parent e3b4119785
commit 4f472633e6
3 changed files with 724 additions and 42 deletions
+27 -27
View File
@@ -13,12 +13,26 @@
- [x] Circular Buffer - [x] Circular Buffer
- [ ] Deque (segmented array), ⛔ Not possible in Go - [ ] Deque (segmented array), ⛔ Not possible in Go
> Documentation
```bash
go doc -all ./linear | bat -l go
```
## Tree — hierarchical, parent/child relationships ## Tree — hierarchical, parent/child relationships
- [x] Binary Search Tree - [x] Binary Search Tree
- [x] AVL Tree - [x] AVL Tree
- [x] Heap (min/max) - [x] Heap (min/max)
- [ ] Trie - [ ] Trie
- [ ] LSM Tree
> Documentation
```bash
for p in ./trees/ ./trees/avl ./trees/heap; do
go doc -all "$p"; done | bat -l go
```
## Graph ## Graph
@@ -31,6 +45,12 @@
- [x] Union Find - [x] Union Find
> Documentation
```bash
go doc -all ./sets/ | bat -l go
```
## Probabilistic ## Probabilistic
- [ ] Bloom filter - [ ] Bloom filter
@@ -47,6 +67,12 @@
- [ ] Modular Arithmetic - [ ] Modular Arithmetic
- [ ] Sieve of Eratosthenes - [ ] Sieve of Eratosthenes
> Documentation
```bash
go doc -all ./algo | bat -l go
```
## Heaps & Trees ## Heaps & Trees
- [x] Priority Queue - [x] Priority Queue
@@ -59,33 +85,7 @@
- [x] DFS & BFS - [x] DFS & BFS
- [ ] Topological Sort with Kahn's algorithm - [ ] Topological Sort with Kahn's algorithm
# Documentation > Documentation
> Linear Structures
```bash
go doc -all ./linear | bat -l go
```
> Algorithms
```bash
go doc -all ./algo | bat -l go
```
> Sets
```bash
go doc -all ./sets/ | bat -l go
```
> Trees
```bash
for p in ./trees/ ./trees/avl ./trees/heap; do go doc -all "$p"; done | bat -l go
```
> Graphs
```bash ```bash
go doc -all ./graphs | bat -l go go doc -all ./graphs | bat -l go
+617
View File
@@ -0,0 +1,617 @@
package tests
import (
"bufio"
"io"
"math"
"math/rand"
"os"
"sort"
"strconv"
"strings"
"testing"
"datastructures/trees/avl"
)
// AvlTree exposes Root and AvlNode.Data/Height, so these tests drive the tree
// through Insert and observe it through the two traversals. Inorder of a BST
// must come back sorted, which makes it a cheap oracle for the whole structure:
// if a rotation dropped or duplicated a node, the sorted comparison catches it.
//
// Helpers uniqueSorted, equalInts and validBSTPreorder live in
// binarySearchTree_test.go, same package.
func equalStrings(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func TestAvlTraversalsOnEmptyTree(t *testing.T) {
tree := avl.NewAvlTree[int]()
if got := tree.TraverseInorder(); len(got) != 0 {
t.Errorf("TraverseInorder() on empty tree = %v, want empty", got)
}
if got := tree.TraversePreorder(); len(got) != 0 {
t.Errorf("TraversePreorder() on empty tree = %v, want empty", got)
}
}
func TestAvlTraversalsOnSingleNode(t *testing.T) {
tree := avl.NewAvlTree[int]()
tree.Insert(42)
if got := tree.TraverseInorder(); !equalInts(got, []int{42}) {
t.Errorf("TraverseInorder() = %v, want [42]", got)
}
if got := tree.TraversePreorder(); !equalInts(got, []int{42}) {
t.Errorf("TraversePreorder() = %v, want [42]", got)
}
}
func TestAvlInorderIsSorted(t *testing.T) {
inputs := [][]int{
{5, 3, 8, 1, 4, 7, 9},
{1, 2, 3, 4, 5, 6, 7}, // ascending, forces left rotations
{7, 6, 5, 4, 3, 2, 1}, // descending, forces right rotations
{10, 20, 30, 40, 50, 25}, // mixes both, hits the double rotations
{-5, 0, -10, 3, -3, 8}, // negatives
{42}, // single node
{1, 1, 1, 1}, // all duplicates
}
for _, in := range inputs {
tree := avl.NewAvlTree[int]()
for _, v := range in {
tree.Insert(v)
}
got := tree.TraverseInorder()
want := uniqueSorted(in)
if !equalInts(got, want) {
t.Errorf("insert %v: TraverseInorder() = %v, want %v", in, got, want)
}
}
}
// The insert sequence below is worked out by hand: it ends as
// 30(20(10,25), 40(_,50)) after a left-right double rotation at the root, so
// the exact preorder pins down the tree shape, not just its contents.
func TestAvlPreorderMatchesKnownShape(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{10, 20, 30, 40, 50, 25} {
tree.Insert(v)
}
want := []int{30, 20, 10, 25, 40, 50}
if got := tree.TraversePreorder(); !equalInts(got, want) {
t.Errorf("TraversePreorder() = %v, want %v", got, want)
}
wantInorder := []int{10, 20, 25, 30, 40, 50}
if got := tree.TraverseInorder(); !equalInts(got, wantInorder) {
t.Errorf("TraverseInorder() = %v, want %v", got, wantInorder)
}
}
func TestAvlPreorderStartsAtRoot(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{50, 25, 75, 10, 30, 60, 90} {
tree.Insert(v)
}
got := tree.TraversePreorder()
if len(got) == 0 {
t.Fatal("TraversePreorder() returned nothing")
}
if got[0] != tree.Root.Data {
t.Errorf("TraversePreorder()[0] = %d, want root %d", got[0], tree.Root.Data)
}
}
func TestAvlPreorderIsValidBSTPreorder(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{15, 3, 27, 1, 9, 20, 40, 6, 12, 35, 50} {
tree.Insert(v)
}
pre := tree.TraversePreorder()
if !validBSTPreorder(pre) {
t.Errorf("TraversePreorder() = %v is not a valid BST pre-order", pre)
}
}
// Both traversals must visit every node exactly once, so they agree on length
// and on contents once sorted.
func TestAvlTraversalsVisitSameNodes(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{8, 2, 19, 4, 11, 25, 1, 6, 30, 15} {
tree.Insert(v)
}
inorder := tree.TraverseInorder()
preorder := tree.TraversePreorder()
if len(inorder) != len(preorder) {
t.Fatalf("length mismatch: inorder %d, preorder %d", len(inorder), len(preorder))
}
sortedPre := append([]int(nil), preorder...)
sort.Ints(sortedPre)
if !equalInts(inorder, sortedPre) {
t.Errorf("traversals disagree: inorder %v, sorted preorder %v", inorder, sortedPre)
}
}
func TestAvlTraversalsDoNotMutateTree(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{5, 2, 9, 1, 7, 12} {
tree.Insert(v)
}
rootBefore := tree.Root
heightBefore := tree.Root.Height
first := tree.TraverseInorder()
second := tree.TraverseInorder()
firstPre := tree.TraversePreorder()
secondPre := tree.TraversePreorder()
if !equalInts(first, second) {
t.Errorf("repeated TraverseInorder() differ: %v then %v", first, second)
}
if !equalInts(firstPre, secondPre) {
t.Errorf("repeated TraversePreorder() differ: %v then %v", firstPre, secondPre)
}
if tree.Root != rootBefore || tree.Root.Height != heightBefore {
t.Error("traversal changed the tree root")
}
}
// The constraint moved from a numeric-only interface to cmp.Ordered, so strings
// have to work end to end.
func TestAvlStringKeys(t *testing.T) {
tree := avl.NewAvlTree[string]()
for _, w := range []string{"pear", "apple", "fig", "date", "banana", "cherry"} {
tree.Insert(w)
}
want := []string{"apple", "banana", "cherry", "date", "fig", "pear"}
if got := tree.TraverseInorder(); !equalStrings(got, want) {
t.Errorf("TraverseInorder() = %v, want %v", got, want)
}
if got := tree.TraversePreorder(); len(got) != len(want) {
t.Errorf("TraversePreorder() returned %d values, want %d", len(got), len(want))
}
}
func TestAvlFloatKeys(t *testing.T) {
tree := avl.NewAvlTree[float64]()
for _, v := range []float64{3.5, -1.25, 0, 9.75, 2.5} {
tree.Insert(v)
}
want := []float64{-1.25, 0, 2.5, 3.5, 9.75}
got := tree.TraverseInorder()
if len(got) != len(want) {
t.Fatalf("TraverseInorder() = %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("TraverseInorder() = %v, want %v", got, want)
}
}
}
// Degenerate insert orders are what the rotations exist for: 1..n ascending
// would be a linked list in a plain BST. Height must stay within the AVL bound
// of 1.44*log2(n+2).
func TestAvlStaysBalancedOnSortedInput(t *testing.T) {
const count = 1000
for _, name := range []string{"ascending", "descending"} {
tree := avl.NewAvlTree[int]()
for i := 0; i < count; i++ {
if name == "ascending" {
tree.Insert(i)
} else {
tree.Insert(count - i)
}
}
got := tree.TraverseInorder()
if len(got) != count {
t.Fatalf("%s: TraverseInorder() returned %d values, want %d", name, len(got), count)
}
if !sort.IntsAreSorted(got) {
t.Fatalf("%s: TraverseInorder() is not sorted", name)
}
maxHeight := int(1.44 * math.Log2(float64(count+2)))
if tree.Root.Height > maxHeight {
t.Errorf("%s: height %d exceeds AVL bound %d", name, tree.Root.Height, maxHeight)
}
}
}
func TestAvlTraversalsRandomized(t *testing.T) {
const inserts = 3000
tree := avl.NewAvlTree[int]()
var raw []int
for i := 0; i < inserts; i++ {
v := rand.Intn(750)
tree.Insert(v)
raw = append(raw, v)
}
want := uniqueSorted(raw)
if got := tree.TraverseInorder(); !equalInts(got, want) {
t.Errorf("TraverseInorder() has %d values, want %d unique", len(got), len(want))
}
pre := tree.TraversePreorder()
if len(pre) != len(want) {
t.Errorf("TraversePreorder() returned %d values, want %d", len(pre), len(want))
}
if !validBSTPreorder(pre) {
t.Error("TraversePreorder() is not a valid BST pre-order")
}
}
// --- Insert ---
// checkAvlInvariants walks the tree recomputing height from the leaves up and
// returns the true height. It fails the test if any node's stored Height is
// stale, if any balance factor leaves [-1,1], or if the BST ordering is broken.
func checkAvlInvariants(t *testing.T, node *avl.AvlNode[int], low, high int) int {
t.Helper()
if node == nil {
return 0
}
if node.Data <= low || node.Data >= high {
t.Errorf("node %d violates BST ordering, must be in (%d, %d)", node.Data, low, high)
}
leftHeight := checkAvlInvariants(t, node.Left, low, node.Data)
rightHeight := checkAvlInvariants(t, node.Right, node.Data, high)
realHeight := 1 + max(leftHeight, rightHeight)
if node.Height != realHeight {
t.Errorf("node %d has stored Height %d, recomputed %d", node.Data, node.Height, realHeight)
}
if balance := leftHeight - rightHeight; balance < -1 || balance > 1 {
t.Errorf("node %d has balance factor %d, want within [-1,1]", node.Data, balance)
}
return realHeight
}
// Each sequence below triggers exactly one of the four rebalancing cases, and
// all four settle into the same 2(1,3) shape.
func TestAvlInsertRotationCases(t *testing.T) {
cases := []struct {
name string
insert []int
}{
{"left-left", []int{3, 2, 1}},
{"right-right", []int{1, 2, 3}},
{"left-right", []int{3, 1, 2}},
{"right-left", []int{1, 3, 2}},
}
for _, c := range cases {
tree := avl.NewAvlTree[int]()
for _, v := range c.insert {
tree.Insert(v)
}
if got := tree.TraversePreorder(); !equalInts(got, []int{2, 1, 3}) {
t.Errorf("%s: inserting %v gave preorder %v, want [2 1 3]", c.name, c.insert, got)
}
if tree.Root.Height != 2 {
t.Errorf("%s: root height = %d, want 2", c.name, tree.Root.Height)
}
checkAvlInvariants(t, tree.Root, math.MinInt, math.MaxInt)
}
}
func TestAvlInsertIgnoresDuplicates(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{5, 3, 8, 5, 3, 8, 5} {
tree.Insert(v)
}
want := []int{3, 5, 8}
if got := tree.TraverseInorder(); !equalInts(got, want) {
t.Errorf("TraverseInorder() = %v, want %v", got, want)
}
if tree.Root.Height != 2 {
t.Errorf("root height = %d, want 2 (duplicates must not deepen the tree)", tree.Root.Height)
}
}
func TestAvlInsertSetsRootAndLeafHeights(t *testing.T) {
tree := avl.NewAvlTree[int]()
if tree.Root != nil {
t.Fatal("NewAvlTree() should start with a nil Root")
}
tree.Insert(1)
if tree.Root == nil {
t.Fatal("Insert() did not set Root")
}
if tree.Root.Data != 1 {
t.Errorf("Root.Data = %d, want 1", tree.Root.Data)
}
if tree.Root.Height != 1 {
t.Errorf("leaf Height = %d, want 1", tree.Root.Height)
}
if tree.Root.Left != nil || tree.Root.Right != nil {
t.Error("a single-node tree should have no children")
}
}
// Insert order must not matter: the same set of keys always produces the same
// AVL tree only if the rotations are right, but at minimum every order has to
// yield the same contents and hold the invariants.
func TestAvlInsertOrderIndependence(t *testing.T) {
orders := [][]int{
{1, 2, 3, 4, 5, 6, 7},
{7, 6, 5, 4, 3, 2, 1},
{4, 2, 6, 1, 3, 5, 7},
{1, 7, 2, 6, 3, 5, 4},
}
want := []int{1, 2, 3, 4, 5, 6, 7}
for _, order := range orders {
tree := avl.NewAvlTree[int]()
for _, v := range order {
tree.Insert(v)
}
if got := tree.TraverseInorder(); !equalInts(got, want) {
t.Errorf("insert %v: TraverseInorder() = %v, want %v", order, got, want)
}
checkAvlInvariants(t, tree.Root, math.MinInt, math.MaxInt)
}
}
func TestAvlInsertKeepsInvariantsRandomized(t *testing.T) {
tree := avl.NewAvlTree[int]()
for i := 0; i < 2000; i++ {
tree.Insert(rand.Intn(5000))
}
checkAvlInvariants(t, tree.Root, math.MinInt, math.MaxInt)
}
// --- Find ---
func TestAvlFindOnEmptyTree(t *testing.T) {
tree := avl.NewAvlTree[int]()
node, ok := tree.Find(1)
if ok {
t.Error("Find() on an empty tree returned true")
}
if node == nil {
t.Fatal("Find() returned a nil node; the contract is a zero node and false")
}
if node.Data != 0 {
t.Errorf("Find() miss returned Data %d, want the zero value", node.Data)
}
}
func TestAvlFindLocatesEveryInsertedValue(t *testing.T) {
values := []int{50, 25, 75, 10, 30, 60, 90, 5, 15, 27, 40}
tree := avl.NewAvlTree[int]()
for _, v := range values {
tree.Insert(v)
}
for _, v := range values {
node, ok := tree.Find(v)
if !ok {
t.Errorf("Find(%d) = false, want true", v)
continue
}
if node.Data != v {
t.Errorf("Find(%d) returned node holding %d", v, node.Data)
}
}
}
func TestAvlFindMissingValues(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{50, 25, 75, 10, 30} {
tree.Insert(v)
}
for _, missing := range []int{-1, 0, 11, 26, 49, 51, 74, 76, 1000} {
if _, ok := tree.Find(missing); ok {
t.Errorf("Find(%d) = true, want false", missing)
}
}
}
// Find walks the tree by reassigning t.Root on a value receiver. That is only
// safe because the receiver is a copy; this test would catch a switch to a
// pointer receiver, which would leave the tree truncated after one lookup.
func TestAvlFindDoesNotMutateTree(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{50, 25, 75, 10, 30, 60, 90} {
tree.Insert(v)
}
before := tree.TraverseInorder()
rootBefore := tree.Root
tree.Find(90)
tree.Find(10)
tree.Find(-1) // miss, walks all the way to a nil child
if tree.Root != rootBefore {
t.Error("Find() moved the tree Root")
}
if after := tree.TraverseInorder(); !equalInts(before, after) {
t.Errorf("tree changed after Find(): %v then %v", before, after)
}
}
func TestAvlFindStringKeys(t *testing.T) {
tree := avl.NewAvlTree[string]()
for _, w := range []string{"pear", "apple", "fig"} {
tree.Insert(w)
}
if node, ok := tree.Find("apple"); !ok || node.Data != "apple" {
t.Errorf(`Find("apple") = %v, %v; want the apple node and true`, node, ok)
}
if _, ok := tree.Find("kiwi"); ok {
t.Error(`Find("kiwi") = true, want false`)
}
}
// --- Display ---
// captureAvlDisplay runs tree.Display() with stdout redirected and returns the
// printed lines. captureDisplay in binarySearchTree_test.go is for BSTree.
func captureAvlDisplay(tree *avl.AvlTree[int]) []string {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
tree.Display()
w.Close()
os.Stdout = old
var lines []string
scanner := bufio.NewScanner(r)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
_, _ = io.Copy(io.Discard, r)
return lines
}
func TestAvlDisplayEmptyTree(t *testing.T) {
tree := avl.NewAvlTree[int]()
lines := captureAvlDisplay(tree)
if len(lines) != 1 || lines[0] != "<empty>" {
t.Errorf("Display() on empty tree printed %q, want [<empty>]", lines)
}
}
func TestAvlDisplaySingleNode(t *testing.T) {
tree := avl.NewAvlTree[int]()
tree.Insert(7)
lines := captureAvlDisplay(tree)
if len(lines) != 1 || lines[0] != "7" {
t.Errorf("Display() printed %q, want [7] with no connector", lines)
}
}
// Golden output for the hand-checked 30(20(10,25), 40(_,50)) tree. Display
// draws it rotated 90° left, so the right subtree sits on top.
func TestAvlDisplayKnownTree(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{10, 20, 30, 40, 50, 25} {
tree.Insert(v)
}
want := []string{
" ┌── 50",
" ┌── 40",
"30",
" │ ┌── 25",
" └── 20",
" └── 10",
}
got := captureAvlDisplay(tree)
if !equalStrings(got, want) {
t.Errorf("Display() printed:\n%s\nwant:\n%s",
strings.Join(got, "\n"), strings.Join(want, "\n"))
}
}
// Rotated 90° left means reading the lines top to bottom yields a reverse
// in-order walk, which ties Display back to the traversals.
func TestAvlDisplayReadsAsReverseInorder(t *testing.T) {
tree := avl.NewAvlTree[int]()
for _, v := range []int{15, 3, 27, 1, 9, 20, 40, 6, 12, 35, 50} {
tree.Insert(v)
}
lines := captureAvlDisplay(tree)
var printed []int
for _, line := range lines {
fields := strings.Fields(line)
value, err := strconv.Atoi(fields[len(fields)-1])
if err != nil {
t.Fatalf("could not read a value from Display() line %q: %v", line, err)
}
printed = append(printed, value)
}
inorder := tree.TraverseInorder()
want := make([]int, 0, len(inorder))
for i := len(inorder) - 1; i >= 0; i-- {
want = append(want, inorder[i])
}
if !equalInts(printed, want) {
t.Errorf("Display() values top to bottom = %v, want reverse inorder %v", printed, want)
}
}
+80 -15
View File
@@ -1,31 +1,30 @@
package avl package avl
import "fmt" import (
"cmp"
"fmt"
type numericTypes interface { "datastructures/linear"
int | int8 | int16 | int32 | int64 | )
uint | uint8 | uint16 | uint32 | uint64 |
float32 | float64
}
type AvlNode[T numericTypes] struct { type AvlNode[T cmp.Ordered] struct {
Left *AvlNode[T] Left *AvlNode[T]
Right *AvlNode[T] Right *AvlNode[T]
Data T Data T
Height int Height int
} }
type AvlTree[T numericTypes] struct { type AvlTree[T cmp.Ordered] struct {
Root *AvlNode[T] Root *AvlNode[T]
} }
// NewAvlTree() -> creates an AVL tree // NewAvlTree() -> creates an AVL tree
func NewAvlTree[T numericTypes]() *AvlTree[T] { func NewAvlTree[T cmp.Ordered]() *AvlTree[T] {
return &AvlTree[T]{} return &AvlTree[T]{}
} }
// height() -> 0 if root // height() -> 0 if root
func height[T numericTypes](n *AvlNode[T]) int { func height[T cmp.Ordered](n *AvlNode[T]) int {
if n == nil { if n == nil {
return 0 return 0
} }
@@ -33,12 +32,12 @@ func height[T numericTypes](n *AvlNode[T]) int {
} }
// updateHeight() // updateHeight()
func updateHeight[T numericTypes](node *AvlNode[T]) { func updateHeight[T cmp.Ordered](node *AvlNode[T]) {
node.Height = 1 + max(height(node.Left), height(node.Right)) node.Height = 1 + max(height(node.Left), height(node.Right))
} }
// balanceFactor() -> only 0 , -1 ,-2 ok // balanceFactor() -> only 0 , -1 ,-2 ok
func balanceFactor[T numericTypes](n *AvlNode[T]) int { func balanceFactor[T cmp.Ordered](n *AvlNode[T]) int {
if n == nil { if n == nil {
return 0 return 0
} }
@@ -47,7 +46,7 @@ func balanceFactor[T numericTypes](n *AvlNode[T]) int {
} }
// rotateRight() -> fixes a left heavy subtree, returns the new subtree root // rotateRight() -> fixes a left heavy subtree, returns the new subtree root
func rotateRight[T numericTypes](y *AvlNode[T]) *AvlNode[T] { func rotateRight[T cmp.Ordered](y *AvlNode[T]) *AvlNode[T] {
x := y.Left x := y.Left
b := x.Right b := x.Right
@@ -61,7 +60,7 @@ func rotateRight[T numericTypes](y *AvlNode[T]) *AvlNode[T] {
} }
// rotateLeft() -> fixes a right heavy subtree, returns the new subtree root // rotateLeft() -> fixes a right heavy subtree, returns the new subtree root
func rotateLeft[T numericTypes](x *AvlNode[T]) *AvlNode[T] { func rotateLeft[T cmp.Ordered](x *AvlNode[T]) *AvlNode[T] {
y := x.Right y := x.Right
b := y.Left b := y.Left
@@ -79,7 +78,7 @@ func (tree *AvlTree[T]) Insert(data T) {
tree.Root = insertHelper(tree.Root, data) tree.Root = insertHelper(tree.Root, data)
} }
func insertHelper[T numericTypes](node *AvlNode[T], data T) *AvlNode[T] { func insertHelper[T cmp.Ordered](node *AvlNode[T], data T) *AvlNode[T] {
// normal BST insert // normal BST insert
if node == nil { if node == nil {
return &AvlNode[T]{Data: data, Height: 1} return &AvlNode[T]{Data: data, Height: 1}
@@ -136,6 +135,72 @@ func (t AvlTree[T]) Find(val T) (*AvlNode[T], bool) {
return zero, false return zero, false
} }
// Traversals
// TraverseInorder() -> in order traversal of avl tree starting at root, returns a slice of
// AvlNode.Data. Iterative, no recursion.
func (tree *AvlTree[T]) TraverseInorder() []T {
var list []T
stack := linear.Stack[*AvlNode[T]]{}
current := tree.Root
// every node on the stack has been walked past but not emitted yet
for current != nil || stack.Size() > 0 {
// dive left
for current != nil {
stack.Push(current)
current = current.Left
}
node, err := stack.Pop()
if err != nil {
break
}
list = append(list, node.Data)
current = node.Right
}
return list
}
// TraversePreorder() -> pre order traversal of avl tree starting at root, returns a slice of
// AvlNode.Data
func (tree *AvlTree[T]) TraversePreorder() []T {
var list []T
stack := linear.Stack[*AvlNode[T]]{}
if tree.Root == nil {
return list
}
stack.Push(tree.Root)
for stack.Size() > 0 {
root, err := stack.Pop()
if err != nil {
break
}
list = append(list, root.Data)
if root.Right != nil {
stack.Push(root.Right)
}
if root.Left != nil {
stack.Push(root.Left)
}
}
return list
}
// Display() -> draws the tree rotated 90° left: right subtree on top, // Display() -> draws the tree rotated 90° left: right subtree on top,
// left subtree on the bottom, connected with box-drawing characters. // left subtree on the bottom, connected with box-drawing characters.
// Iterative , no recursion. // Iterative , no recursion.