go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/collections/binary_search_tree_test.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package collections 9 10 import ( 11 "cmp" 12 "fmt" 13 "strings" 14 "testing" 15 16 . "go.charczuk.com/sdk/assert" 17 ) 18 19 func Test_BinarySearchTree_basic(t *testing.T) { 20 bst := new(BinarySearchTree[int, string]) 21 for x := 0; x < 100; x++ { 22 bst.Insert(makeKeyValue(x)()) 23 } 24 25 minKey, minValue, ok := bst.Min() 26 ItsEqual(t, true, ok) 27 ItsEqual(t, 0, minKey) 28 ItsEqual(t, "value-0", minValue) 29 30 maxKey, maxValue, ok := bst.Max() 31 ItsEqual(t, true, ok) 32 ItsEqual(t, 99, maxKey) 33 ItsEqual(t, "value-99", maxValue) 34 35 for x := 0; x < 100; x++ { 36 value, ok := bst.Search(x) 37 ItsEqual(t, true, ok) 38 ItsEqual(t, fmt.Sprintf("value-%d", x), value) 39 } 40 41 _, ok = bst.Search(-1) 42 ItsEqual(t, false, ok) 43 44 _, ok = bst.Search(101) 45 ItsEqual(t, false, ok) 46 47 for x := 0; x < 100; x++ { 48 bst.Delete(x) 49 } 50 51 for x := 0; x < 100; x++ { 52 _, ok = bst.Search(x) 53 ItsEqual(t, false, ok) 54 } 55 56 // test insert overwrite 57 bst.Insert(makeKeyValue(10)()) 58 bst.Insert(makeKeyValue(20)()) 59 60 bst.Insert(10, "not-value-10") 61 62 value, ok := bst.Search(10) 63 ItsEqual(t, true, ok) 64 ItsEqual(t, "not-value-10", value) 65 66 value, ok = bst.Search(20) 67 ItsEqual(t, true, ok) 68 ItsEqual(t, "value-20", value) 69 } 70 71 func Test_BinarySearchTree_acceptance(t *testing.T) { 72 inputs := []int{ 73 81, 87, 47, 59, 83, 74 18, 25, 40, 56, 0, 75 94, 11, 62, 89, 28, 76 74, 12, 45, 37, 6, 77 95, 66, 29, 58, 101, 78 } 79 80 bst := new(BinarySearchTree[int, string]) 81 for _, x := range inputs { 82 bst.Insert(makeKeyValue(x)()) 83 } 84 85 node := func(k, height int, left, right *BinarySearchTreeNode[int, string]) *BinarySearchTreeNode[int, string] { 86 return &BinarySearchTreeNode[int, string]{ 87 Key: k, 88 Height: height, 89 Left: left, 90 Right: right, 91 } 92 } 93 94 goldenBST := &BinarySearchTree[int, string]{ 95 root: node(47, 5, 96 node(25, 4, 97 node(11, 3, 98 node(0, 2, nil, node(6, 1, nil, nil)), 99 node(18, 2, node(12, 1, nil, nil), nil), 100 ), 101 node(40, 3, 102 node(29, 2, node(28, 1, nil, nil), node(37, 1, nil, nil)), 103 node(45, 1, nil, nil), 104 ), 105 ), 106 node(81, 4, 107 node(59, 3, 108 node(56, 2, nil, node(58, 1, nil, nil)), 109 node(66, 2, 110 node(62, 1, nil, nil), 111 node(74, 1, nil, nil), 112 ), 113 ), 114 node(94, 3, 115 node(87, 2, 116 node(83, 1, nil, nil), 117 node(89, 1, nil, nil), 118 ), 119 node(95, 2, nil, node(101, 1, nil, nil)), 120 ), 121 ), 122 ), 123 } 124 ItsEqual(t, printTree(goldenBST), printTree(bst)) 125 ItsEqual(t, true, goldenBST.KeysEqual(bst), fmt.Sprintf("%s vs. %s", printTree(bst), printTree(goldenBST))) 126 127 // delete 3 nodes, causing a rotation on the right side 128 129 bst.Delete(94) 130 bst.Delete(95) 131 132 bst.Delete(89) 133 bst.Delete(101) 134 bst.Delete(83) 135 136 goldenBST2 := &BinarySearchTree[int, string]{ 137 root: node(47, 5, 138 node(25, 4, 139 node(11, 3, 140 node(0, 2, nil, node(6, 1, nil, nil)), 141 node(18, 2, node(12, 1, nil, nil), nil), 142 ), 143 node(40, 3, 144 node(29, 2, node(28, 1, nil, nil), node(37, 1, nil, nil)), 145 node(45, 1, nil, nil), 146 ), 147 ), 148 node(59, 4, 149 node(56, 2, 150 nil, 151 node(58, 1, nil, nil), 152 ), 153 node(81, 3, 154 node(66, 2, 155 node(62, 1, nil, nil), 156 node(74, 1, nil, nil), 157 ), 158 node(87, 1, nil, nil), 159 ), 160 ), 161 ), 162 } 163 164 ItsEqual(t, printTree(goldenBST2), printTree(bst)) 165 ItsEqual(t, true, goldenBST2.KeysEqual(bst), fmt.Sprintf("%s vs. %s", printTree(bst), printTree(goldenBST2))) 166 } 167 168 func Test_BinarySearchTree_InOrder(t *testing.T) { 169 bst := new(BinarySearchTree[int, string]) 170 for x := 0; x < 5; x++ { 171 bst.Insert(makeKeyValue(x)()) 172 } 173 ItsEqual(t, 1, bst.root.Key) 174 175 var keys []int 176 bst.InOrder(func(k int, _ string) { 177 keys = append(keys, k) 178 }) 179 ItsEqual(t, []int{0, 1, 2, 3, 4}, keys) 180 } 181 182 func Test_BinarySearchTree_PreOrder(t *testing.T) { 183 bst := new(BinarySearchTree[int, string]) 184 for x := 0; x < 5; x++ { 185 bst.Insert(makeKeyValue(x)()) 186 } 187 ItsEqual(t, 1, bst.root.Key) 188 189 var keys []int 190 bst.PreOrder(func(k int, _ string) { 191 keys = append(keys, k) 192 }) 193 ItsEqual(t, []int{1, 0, 3, 2, 4}, keys) 194 } 195 196 func Test_BinarySearchTree_PostOrder(t *testing.T) { 197 bst := new(BinarySearchTree[int, string]) 198 for x := 0; x < 5; x++ { 199 bst.Insert(makeKeyValue(x)()) 200 } 201 ItsEqual(t, 1, bst.root.Key) 202 203 var keys []int 204 bst.PostOrder(func(k int, _ string) { 205 keys = append(keys, k) 206 }) 207 ItsEqual(t, []int{0, 2, 4, 3, 1}, keys) 208 } 209 210 func Test_BinarySearchTree_unbalancedLeft(t *testing.T) { 211 bst := new(BinarySearchTree[int, string]) 212 for x := 0; x < 100; x++ { 213 bst.Insert(makeKeyValue(x)()) 214 } 215 216 for x := 0; x < 50; x++ { 217 bst.Delete(x) 218 } 219 220 ItsEqual(t, 79, bst.root.Key) 221 } 222 223 func Test_BinarySearchTree_reverse(t *testing.T) { 224 bst := new(BinarySearchTree[int, string]) 225 for x := 99; x >= 0; x-- { 226 bst.Insert(makeKeyValue(x)()) 227 } 228 ItsEqual(t, nil, verify(bst.root)) 229 ItsEqual(t, 36, bst.root.Key) 230 } 231 232 // 233 // bst test helpers 234 // 235 236 func makeKeyValue(x int) func() (int, string) { 237 return func() (int, string) { 238 return x, fmt.Sprintf("value-%d", x) 239 } 240 } 241 242 func printTree[K cmp.Ordered, V any](bst *BinarySearchTree[K, V]) string { 243 var keys []string 244 bst.InOrder(func(k K, _ V) { 245 keys = append(keys, fmt.Sprint(k)) 246 }) 247 return "[ " + strings.Join(keys, " ") + " ]" 248 } 249 250 func verify[K cmp.Ordered, V any](n *BinarySearchTreeNode[K, V]) error { 251 if n.Left != nil { 252 min := verifyMin(n) 253 if min.Key >= n.Key { 254 return fmt.Errorf("invalid min; %v is greater than parent %v", min.Key, n.Key) 255 } 256 if err := verify(n.Left); err != nil { 257 return err 258 } 259 } 260 if n.Right != nil { 261 max := verifyMax(n) 262 if max.Key <= n.Key { 263 return fmt.Errorf("invalid max; %v is less than parent %v", max.Key, n.Key) 264 } 265 if err := verify(n.Right); err != nil { 266 return err 267 } 268 } 269 return nil 270 } 271 272 func verifyMin[K cmp.Ordered, V any](n *BinarySearchTreeNode[K, V]) (min *BinarySearchTreeNode[K, V]) { 273 min = n 274 for min.Left != nil { 275 min = min.Left 276 } 277 return 278 } 279 280 func verifyMax[K cmp.Ordered, V any](n *BinarySearchTreeNode[K, V]) (max *BinarySearchTreeNode[K, V]) { 281 max = n 282 for max.Right != nil { 283 max = max.Right 284 } 285 return 286 } 287 288 func Test_max(t *testing.T) { 289 v := max[int]() 290 ItsEqual(t, 0, v) 291 292 v = max(1) 293 ItsEqual(t, 1, v) 294 295 v = max(1, 2, 3, 4) 296 ItsEqual(t, 4, v) 297 } 298 299 func Test_keysEqual(t *testing.T) { 300 bst := new(BinarySearchTree[int, func()]) 301 ItsEqual(t, true, bst._keysEqual(nil, nil)) 302 303 a := &BinarySearchTreeNode[int, func()]{ 304 Key: 1, 305 } 306 307 ItsEqual(t, false, bst._keysEqual(a, nil)) 308 ItsEqual(t, false, bst._keysEqual(nil, a)) 309 310 b := &BinarySearchTreeNode[int, func()]{ 311 Key: 2, 312 } 313 ItsEqual(t, false, bst._keysEqual(a, b)) 314 315 a1 := &BinarySearchTreeNode[int, func()]{ 316 Key: 1, 317 Height: 10, 318 } 319 ItsEqual(t, false, bst._keysEqual(a, a1)) 320 321 a0 := &BinarySearchTreeNode[int, func()]{ 322 Key: 1, 323 } 324 ItsEqual(t, true, bst._keysEqual(a, a0)) 325 }