diff --git a/README.md b/README.md index 9282229..250580c 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,26 @@ - [x] Circular Buffer - [ ] Deque (segmented array), ⛔ Not possible in Go +> Documentation + +```bash +go doc -all ./linear | bat -l go +``` + ## Tree — hierarchical, parent/child relationships - [x] Binary Search Tree - [x] AVL Tree - [x] Heap (min/max) - [ ] Trie +- [ ] LSM Tree + +> Documentation + +```bash +for p in ./trees/ ./trees/avl ./trees/heap; do +go doc -all "$p"; done | bat -l go +``` ## Graph @@ -31,6 +45,12 @@ - [x] Union Find +> Documentation + +```bash +go doc -all ./sets/ | bat -l go +``` + ## Probabilistic - [ ] Bloom filter @@ -47,6 +67,12 @@ - [ ] Modular Arithmetic - [ ] Sieve of Eratosthenes +> Documentation + +```bash +go doc -all ./algo | bat -l go +``` + ## Heaps & Trees - [x] Priority Queue @@ -59,33 +85,7 @@ - [x] DFS & BFS - [ ] Topological Sort with Kahn's algorithm -# 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 +> Documentation ```bash go doc -all ./graphs | bat -l go diff --git a/tests/treeTests/avlTree_test.go b/tests/treeTests/avlTree_test.go new file mode 100644 index 0000000..18c2795 --- /dev/null +++ b/tests/treeTests/avlTree_test.go @@ -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] != "" { + t.Errorf("Display() on empty tree printed %q, want []", 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) + } +} diff --git a/trees/avl/AvlTree.go b/trees/avl/AvlTree.go index 4945a8e..0f2eb06 100644 --- a/trees/avl/AvlTree.go +++ b/trees/avl/AvlTree.go @@ -1,31 +1,30 @@ package avl -import "fmt" +import ( + "cmp" + "fmt" -type numericTypes interface { - int | int8 | int16 | int32 | int64 | - uint | uint8 | uint16 | uint32 | uint64 | - float32 | float64 -} + "datastructures/linear" +) -type AvlNode[T numericTypes] struct { +type AvlNode[T cmp.Ordered] struct { Left *AvlNode[T] Right *AvlNode[T] Data T Height int } -type AvlTree[T numericTypes] struct { +type AvlTree[T cmp.Ordered] struct { Root *AvlNode[T] } // NewAvlTree() -> creates an AVL tree -func NewAvlTree[T numericTypes]() *AvlTree[T] { +func NewAvlTree[T cmp.Ordered]() *AvlTree[T] { return &AvlTree[T]{} } // height() -> 0 if root -func height[T numericTypes](n *AvlNode[T]) int { +func height[T cmp.Ordered](n *AvlNode[T]) int { if n == nil { return 0 } @@ -33,12 +32,12 @@ func height[T numericTypes](n *AvlNode[T]) int { } // 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)) } // 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 { 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 -func rotateRight[T numericTypes](y *AvlNode[T]) *AvlNode[T] { +func rotateRight[T cmp.Ordered](y *AvlNode[T]) *AvlNode[T] { x := y.Left 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 -func rotateLeft[T numericTypes](x *AvlNode[T]) *AvlNode[T] { +func rotateLeft[T cmp.Ordered](x *AvlNode[T]) *AvlNode[T] { y := x.Right b := y.Left @@ -79,7 +78,7 @@ func (tree *AvlTree[T]) Insert(data T) { 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 if node == nil { return &AvlNode[T]{Data: data, Height: 1} @@ -136,6 +135,72 @@ func (t AvlTree[T]) Find(val T) (*AvlNode[T], bool) { 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, // left subtree on the bottom, connected with box-drawing characters. // Iterative , no recursion.