├── .github └── workflows │ └── test.yaml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── binarytree.go ├── binarytree_test.go ├── go.mod ├── go.sum ├── images └── binarytree.svg └── vendor ├── gopkg.in └── dnaeon │ └── go-deque.v1 │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── README.org │ └── deque.go └── modules.txt /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | # .github/workflows/test.yaml 2 | on: [push, pull_request] 3 | name: test 4 | jobs: 5 | test: 6 | strategy: 7 | matrix: 8 | go-version: [1.18.x, 1.19.x] 9 | os: [ubuntu-latest] 10 | runs-on: ${{ matrix.os }} 11 | steps: 12 | - uses: actions/setup-go@v3 13 | with: 14 | go-version: ${{ matrix.go-version }} 15 | - uses: actions/checkout@v3 16 | - run: make test_cover 17 | - name: Upload coverage to Codecov 18 | uses: codecov/codecov-action@v3 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Emacs backup files 2 | *~ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Marin Atanasov Nikolov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer 10 | in this position and unchanged. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | get: 2 | go get -v -t -d ./... 3 | 4 | test: 5 | go test -v -race ./... 6 | 7 | test_cover: 8 | go test -v -race -coverprofile=coverage.txt -covermode=atomic ./... 9 | 10 | .PHONY: get test test_cover 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-binarytree 2 | 3 | [![Build Status](https://github.com/dnaeon/go-binarytree/actions/workflows/test.yaml/badge.svg)](https://github.com/dnaeon/go-binarytree/actions/workflows/test.yaml/badge.svg) 4 | [![Go Reference](https://pkg.go.dev/badge/gopkg.in/dnaeon/go-binarytree.v1.svg)](https://pkg.go.dev/gopkg.in/dnaeon/go-binarytree.v1) 5 | [![Go Report Card](https://goreportcard.com/badge/gopkg.in/dnaeon/go-binarytree.v1)](https://goreportcard.com/report/gopkg.in/dnaeon/go-binarytree.v1) 6 | [![codecov](https://codecov.io/gh/dnaeon/go-binarytree/branch/v1/graph/badge.svg)](https://codecov.io/gh/dnaeon/go-binarytree) 7 | 8 | A simple, generic implementation of [Binary 9 | Trees](https://en.wikipedia.org/wiki/Binary_tree) in Go. 10 | 11 | ![Example Binary Tree](./images/binarytree.svg) 12 | 13 | ## Installation 14 | 15 | Install `go-binarytree` by executing the following command. 16 | 17 | ``` shell 18 | go get -v gopkg.in/dnaeon/go-binarytree.v1 19 | ``` 20 | 21 | ## Usage 22 | 23 | The following example builds a simple binary tree with 7 nodes, and 24 | performs _in-_, _pre-_, _post-_ and _level-order_ walking of the tree 25 | (error handling is omitted for simplicity). 26 | 27 | ``` go 28 | package main 29 | 30 | import ( 31 | "fmt" 32 | 33 | "gopkg.in/dnaeon/go-binarytree.v1" 34 | ) 35 | 36 | func main() { 37 | root := binarytree.NewNode(10) 38 | five := root.InsertLeft(5) 39 | twenty := root.InsertRight(20) 40 | five.InsertLeft(9) 41 | five.InsertRight(18) 42 | twenty.InsertLeft(3) 43 | twenty.InsertRight(7) 44 | 45 | fmt.Printf("height of tree: %d\n", root.Height()) 46 | fmt.Printf("size of the tree: %d\n", root.Size()) 47 | fmt.Printf("tree is balanced: %t\n", root.IsBalancedTree()) 48 | fmt.Printf("tree is complete: %t\n", root.IsCompleteTree()) 49 | fmt.Printf("tree is perfect: %t\n", root.IsPerfectTree()) 50 | 51 | // Function to be called while walking the tree, which simply 52 | // prints the values of each visited node 53 | walkFunc := func(n *binarytree.Node[int]) error { 54 | fmt.Printf("%d ", n.Value) 55 | return nil 56 | } 57 | 58 | fmt.Printf("in-order values: ") 59 | root.WalkInOrder(walkFunc) 60 | fmt.Println() 61 | 62 | fmt.Printf("pre-order values: ") 63 | root.WalkPreOrder(walkFunc) 64 | fmt.Println() 65 | 66 | fmt.Printf("post-orer values: ") 67 | root.WalkPostOrder(walkFunc) 68 | fmt.Println() 69 | 70 | fmt.Printf("level-order values: ") 71 | root.WalkLevelOrder(walkFunc) 72 | fmt.Println() 73 | } 74 | ``` 75 | 76 | Running above example produces the following output. 77 | 78 | ``` shell 79 | height of tree: 2 80 | size of the tree: 7 81 | tree is balanced: true 82 | tree is complete: true 83 | tree is perfect: true 84 | in-order values: 9 5 18 10 3 20 7 85 | pre-order values: 10 5 9 18 20 3 7 86 | post-orer values: 9 18 5 3 7 20 10 87 | level-order values: 10 5 20 9 18 3 7 88 | ``` 89 | 90 | The following example generates the [Dot 91 | representation](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) 92 | of the binary tree and prints it to the standard output. 93 | 94 | ``` go 95 | package main 96 | 97 | import ( 98 | "os" 99 | 100 | "gopkg.in/dnaeon/go-binarytree.v1" 101 | ) 102 | 103 | func main() { 104 | root := binarytree.NewNode(10) 105 | five := root.InsertLeft(5) 106 | twenty := root.InsertRight(20) 107 | five.InsertLeft(9) 108 | five.InsertRight(18) 109 | twenty.InsertLeft(3) 110 | twenty.InsertRight(7) 111 | 112 | root.WriteDot(os.Stdout) 113 | } 114 | ``` 115 | 116 | Running above example produces an output similar to this one. 117 | 118 | ``` shell 119 | digraph { 120 | node [color=lightblue fillcolor=lightblue fontcolor=black shape=record style="filled, rounded"] 121 | 824634441792 [label="| 10|" ] 122 | 824634441792:l -> 824634441856:v 123 | 824634441792:r -> 824634441920:v 124 | 824634441856 [label="| 5|" ] 125 | 824634441856:l -> 824634441984:v 126 | 824634441856:r -> 824634442048:v 127 | 824634441984 [label="| 9|" ] 128 | 824634442048 [label="| 18|" ] 129 | 824634441920 [label="| 20|" ] 130 | 824634441920:l -> 824634442112:v 131 | 824634441920:r -> 824634442176:v 132 | 824634442112 [label="| 3|" ] 133 | 824634442176 [label="| 7|" ] 134 | } 135 | ``` 136 | 137 | The generated representation can be rendered using 138 | [graphviz](https://graphviz.org/), e.g. 139 | 140 | ``` shell 141 | dot -Tsvg /path/to/file.dot -o /tmp/to/file.svg 142 | ``` 143 | 144 | When building a binary tree with user-defined types such as structs, 145 | make sure that you also implement the 146 | [fmt.Stringer](https://pkg.go.dev/fmt#Stringer) interface for your 147 | type, so that Dot generation works properly. 148 | 149 | Make sure to check the included [test cases](./binarytree_test.go) for 150 | additional examples. 151 | 152 | ## Tests 153 | 154 | Run the tests. 155 | 156 | ``` shell 157 | make test 158 | ``` 159 | 160 | ## License 161 | 162 | `go-binarytree` is Open Source and licensed under the [BSD 163 | License](http://opensource.org/licenses/BSD-2-Clause). 164 | -------------------------------------------------------------------------------- /binarytree.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Marin Atanasov Nikolov 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // 1. Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer 9 | // in this position and unchanged. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | // IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | package binarytree 26 | 27 | import ( 28 | "errors" 29 | "fmt" 30 | "io" 31 | "strconv" 32 | "strings" 33 | 34 | deque "gopkg.in/dnaeon/go-deque.v1" 35 | ) 36 | 37 | // WalkFunc is the type of the function which will be invoked while 38 | // visiting a node from the binary tree. 39 | type WalkFunc[T any] func(node *Node[T]) error 40 | 41 | // SkipNodeFunc is a function which returns true, if the currently 42 | // being visited node should be skipped. 43 | type SkipNodeFunc[T any] func(node *Node[T]) bool 44 | 45 | // FindFunc is the type of the function predicate which will be 46 | // invoked for each node while looking for a given node. The function 47 | // should return true, if the node is the one we are looking for, 48 | // false otherwise. 49 | type FindFunc[T any] func(node *Node[T]) bool 50 | 51 | // ComparatorFunc is a function which compares two values of type T. 52 | // The comparator function should return: 53 | // 54 | // - A negative integer when A is less than B 55 | // - A positive integer when A is greater than B 56 | // - Zero when A equals B 57 | // 58 | // The comparator function is used by IsBinarySearchTree method for 59 | // validating whether a given binary tree is a Binary Search Tree 60 | // (BST). 61 | type ComparatorFunc[T any] func(a, b T) int 62 | 63 | // IntComparator is a comparator function for comparing integer node 64 | // values. 65 | func IntComparator(a, b int) int { 66 | if a < b { 67 | return -1 68 | } else if a > b { 69 | return 1 70 | } 71 | 72 | return 0 73 | } 74 | 75 | // StringComparator is a comparator function for comparing string node 76 | // values. 77 | func StringComparator(a, b string) int { 78 | return strings.Compare(a, b) 79 | } 80 | 81 | // Node represents a node from a binary tree 82 | type Node[T any] struct { 83 | // Value is the value of the node 84 | Value T 85 | // Left child of the node 86 | Left *Node[T] 87 | // Right child of the node 88 | Right *Node[T] 89 | 90 | // A list of function handlers, which specify whether a node 91 | // should be skipped or not during tree walking. 92 | skipNodeFuncs []SkipNodeFunc[T] 93 | 94 | // dotAttributes represents the list of attributes associated 95 | // with the node, which will be used when generating the Dot 96 | // representation of the tree. 97 | dotAttributes map[string]string 98 | } 99 | 100 | // NewNode creates a new node 101 | func NewNode[T any](value T) *Node[T] { 102 | node := &Node[T]{ 103 | Value: value, 104 | Left: nil, 105 | Right: nil, 106 | skipNodeFuncs: make([]SkipNodeFunc[T], 0), 107 | dotAttributes: make(map[string]string), 108 | } 109 | 110 | return node 111 | } 112 | 113 | // InsertLeft inserts a new node to the left 114 | func (n *Node[T]) InsertLeft(value T) *Node[T] { 115 | left := NewNode(value) 116 | n.Left = left 117 | 118 | return left 119 | } 120 | 121 | // InsertRight inserts a new node to the right 122 | func (n *Node[T]) InsertRight(value T) *Node[T] { 123 | right := NewNode(value) 124 | n.Right = right 125 | 126 | return right 127 | } 128 | 129 | // WalkInOrder performs an iterative In-order walking of the binary 130 | // tree - Left-Node-Right (LNR) 131 | func (n *Node[T]) WalkInOrder(walkFunc WalkFunc[T]) error { 132 | stack := deque.New[*Node[T]]() 133 | node := n 134 | 135 | for node != nil || !stack.IsEmpty() { 136 | for node != nil { 137 | if n.shouldSkipNode(node) { 138 | node = nil 139 | break 140 | } 141 | stack.PushFront(node) 142 | node = node.Left 143 | } 144 | 145 | if !stack.IsEmpty() { 146 | item, err := stack.PopFront() 147 | if err != nil { 148 | panic(err) 149 | } 150 | 151 | if err := walkFunc(item); err != nil { 152 | return err 153 | } 154 | 155 | node = item.Right 156 | } 157 | } 158 | 159 | return nil 160 | } 161 | 162 | // WalkPreOrder performs an iterative Pre-order walking of the 163 | // binary tree - Node-Left-Right (NLR) 164 | func (n *Node[T]) WalkPreOrder(walkFunc WalkFunc[T]) error { 165 | stack := deque.New[*Node[T]]() 166 | stack.PushFront(n) 167 | 168 | for !stack.IsEmpty() { 169 | node, err := stack.PopFront() 170 | if err != nil { 171 | panic(err) 172 | } 173 | 174 | if n.shouldSkipNode(node) { 175 | continue 176 | } 177 | 178 | if err := walkFunc(node); err != nil { 179 | return err 180 | } 181 | 182 | if node.Right != nil { 183 | stack.PushFront(node.Right) 184 | } 185 | 186 | if node.Left != nil { 187 | stack.PushFront(node.Left) 188 | } 189 | } 190 | 191 | return nil 192 | } 193 | 194 | // WalkPostOrder performs an iterative Post-order walking of the 195 | // binary tree - Left-Right-Node (LRN) 196 | func (n *Node[T]) WalkPostOrder(walkFunc WalkFunc[T]) error { 197 | stack := deque.New[*Node[T]]() 198 | result := deque.New[*Node[T]]() 199 | stack.PushFront(n) 200 | 201 | for !stack.IsEmpty() { 202 | node, err := stack.PopFront() 203 | if err != nil { 204 | panic(err) 205 | } 206 | 207 | if n.shouldSkipNode(node) { 208 | continue 209 | } 210 | 211 | if node.Left != nil { 212 | stack.PushFront(node.Left) 213 | } 214 | if node.Right != nil { 215 | stack.PushFront(node.Right) 216 | } 217 | 218 | result.PushFront(node) 219 | } 220 | 221 | for !result.IsEmpty() { 222 | node, err := result.PopFront() 223 | if err != nil { 224 | return err 225 | } 226 | if err := walkFunc(node); err != nil { 227 | return err 228 | } 229 | } 230 | 231 | return nil 232 | } 233 | 234 | // WalkLevelOrder performs an iterative Level-order (Breadth-first) 235 | // walking of the binary tree. 236 | func (n *Node[T]) WalkLevelOrder(walkFunc WalkFunc[T]) error { 237 | queue := deque.New[*Node[T]]() 238 | queue.PushBack(n) 239 | 240 | for !queue.IsEmpty() { 241 | node, err := queue.PopFront() 242 | if err != nil { 243 | panic(err) 244 | } 245 | 246 | if n.shouldSkipNode(node) { 247 | continue 248 | } 249 | 250 | if err := walkFunc(node); err != nil { 251 | return err 252 | } 253 | 254 | if node.Left != nil { 255 | queue.PushBack(node.Left) 256 | } 257 | if node.Right != nil { 258 | queue.PushBack(node.Right) 259 | } 260 | } 261 | 262 | return nil 263 | } 264 | 265 | // Size returns the size of the tree 266 | func (n *Node[T]) Size() int { 267 | size := 0 268 | walkFunc := func(n *Node[T]) error { 269 | size++ 270 | return nil 271 | } 272 | n.WalkInOrder(walkFunc) 273 | 274 | return size 275 | } 276 | 277 | type nodeHeight[T any] struct { 278 | node *Node[T] 279 | height int 280 | } 281 | 282 | // Height returns the height of the tree 283 | func (n *Node[T]) Height() int { 284 | max_height := 0 285 | root := &nodeHeight[T]{ 286 | node: n, 287 | height: 0, 288 | } 289 | stack := deque.New[*nodeHeight[T]]() 290 | stack.PushFront(root) 291 | 292 | for !stack.IsEmpty() { 293 | item, err := stack.PopFront() 294 | if err != nil { 295 | panic(err) 296 | } 297 | 298 | if item.height > max_height { 299 | max_height = item.height 300 | } 301 | 302 | if item.node.Right != nil { 303 | right := &nodeHeight[T]{ 304 | node: item.node.Right, 305 | height: item.height + 1, 306 | } 307 | stack.PushFront(right) 308 | } 309 | if item.node.Left != nil { 310 | left := &nodeHeight[T]{ 311 | node: item.node.Left, 312 | height: item.height + 1, 313 | } 314 | stack.PushFront(left) 315 | } 316 | } 317 | 318 | return max_height 319 | } 320 | 321 | // IsLeafNode returns true, if the node is a leaf, false otherwise. 322 | func (n *Node[T]) IsLeafNode() bool { 323 | return n.Left == nil && n.Right == nil 324 | } 325 | 326 | // IsFullNode returns true, if the node contains left and right 327 | // children 328 | func (n *Node[T]) IsFullNode() bool { 329 | return n.Left != nil && n.Right != nil 330 | } 331 | 332 | // AddSkipNodeFunc adds a new handler for determining whether a 333 | // node from the tree should be skipped while traversing it. 334 | func (n *Node[T]) AddSkipNodeFunc(handler SkipNodeFunc[T]) { 335 | n.skipNodeFuncs = append(n.skipNodeFuncs, handler) 336 | } 337 | 338 | // shouldSkipNode applies the list of SkipNodeFunc handlers in 339 | // order to determine whether a node should be skipped while walking 340 | // the tree. 341 | func (n *Node[T]) shouldSkipNode(node *Node[T]) bool { 342 | for _, handler := range n.skipNodeFuncs { 343 | if handler(node) { 344 | return true 345 | } 346 | } 347 | 348 | return false 349 | } 350 | 351 | // Find looks for a node in the tree, which satisfies the given 352 | // predicate. 353 | func (n *Node[T]) FindNode(predicate FindFunc[T]) (*Node[T], bool) { 354 | stack := deque.New[*Node[T]]() 355 | stack.PushFront(n) 356 | 357 | for !stack.IsEmpty() { 358 | node, err := stack.PopFront() 359 | if err != nil { 360 | panic(err) 361 | } 362 | 363 | if predicate(node) { 364 | return node, true 365 | } 366 | 367 | if node.Right != nil { 368 | stack.PushFront(node.Right) 369 | } 370 | if node.Left != nil { 371 | stack.PushFront(node.Left) 372 | } 373 | } 374 | 375 | return nil, false 376 | } 377 | 378 | // IsFullTree returns true, if the binary tree is full. A full binary tree 379 | // is a tree in which every node has either 0 or 2 children. 380 | func (n *Node[T]) IsFullTree() bool { 381 | stack := deque.New[*Node[T]]() 382 | stack.PushFront(n) 383 | 384 | for !stack.IsEmpty() { 385 | node, err := stack.PopFront() 386 | if err != nil { 387 | panic(err) 388 | } 389 | if node.IsLeafNode() { 390 | continue 391 | } 392 | 393 | if !node.IsFullNode() { 394 | return false 395 | } 396 | 397 | stack.PushFront(node.Right) 398 | stack.PushFront(node.Left) 399 | } 400 | 401 | return true 402 | } 403 | 404 | // IsDegenerateTree returns true, if each parent has only one child node. 405 | func (n *Node[T]) IsDegenerateTree() bool { 406 | stack := deque.New[*Node[T]]() 407 | stack.PushFront(n) 408 | 409 | for !stack.IsEmpty() { 410 | node, err := stack.PopFront() 411 | if err != nil { 412 | panic(err) 413 | } 414 | if node.IsLeafNode() { 415 | continue 416 | } 417 | 418 | if node.IsFullNode() { 419 | return false 420 | } 421 | 422 | if node.Left != nil { 423 | stack.PushFront(node.Left) 424 | } 425 | if node.Right != nil { 426 | stack.PushFront(node.Right) 427 | } 428 | } 429 | 430 | return true 431 | } 432 | 433 | // IsBalancedTree returns true, if the tree is balanced. A balanced tree 434 | // is such a tree, for which the height of the left and right 435 | // sub-trees of each node differ by no more than 1. 436 | func (n *Node[T]) IsBalancedTree() bool { 437 | if n.IsLeafNode() { 438 | return true 439 | } 440 | 441 | stack := deque.New[*Node[T]]() 442 | stack.PushFront(n) 443 | 444 | for !stack.IsEmpty() { 445 | node, err := stack.PopFront() 446 | if err != nil { 447 | panic(err) 448 | } 449 | 450 | left_height := -1 451 | if node.Left != nil { 452 | left_height = node.Left.Height() 453 | stack.PushFront(node.Left) 454 | } 455 | 456 | right_height := -1 457 | if node.Right != nil { 458 | right_height = node.Right.Height() 459 | stack.PushFront(node.Right) 460 | } 461 | 462 | left_height += 1 463 | right_height += 1 464 | diff := left_height - right_height 465 | if diff < 0 { 466 | diff = -diff 467 | } 468 | 469 | if diff > 1 { 470 | return false 471 | } 472 | } 473 | 474 | return true 475 | } 476 | 477 | // IsCompleteTree returns true, if the tree is complete. A complete 478 | // binary tree is a binary tree in which every level, except possibly 479 | // the last, is completely filled, and all nodes in the last level are 480 | // as far left as possible. 481 | func (n *Node[T]) IsCompleteTree() bool { 482 | if n.IsLeafNode() { 483 | return true 484 | } 485 | 486 | nonFullNodeSeen := false 487 | queue := deque.New[*Node[T]]() 488 | queue.PushBack(n) 489 | 490 | for !queue.IsEmpty() { 491 | node, err := queue.PopFront() 492 | if err != nil { 493 | panic(err) 494 | 495 | } 496 | 497 | if node.Left != nil { 498 | if nonFullNodeSeen { 499 | return false 500 | } 501 | queue.PushBack(node.Left) 502 | } 503 | 504 | if !node.IsFullNode() { 505 | nonFullNodeSeen = true 506 | } 507 | 508 | if node.Right != nil { 509 | if nonFullNodeSeen { 510 | return false 511 | } 512 | queue.PushBack(node.Right) 513 | } 514 | } 515 | 516 | return true 517 | } 518 | 519 | // IsPerfectTree returns true, if the binary tree is full and complete 520 | func (n *Node[T]) IsPerfectTree() bool { 521 | return n.IsFullTree() && n.IsCompleteTree() 522 | } 523 | 524 | // errNotBst is returned by a walking function when a tree being 525 | // walked is detected to not be a BST. 526 | var errNotBst = errors.New("not a binary search tree") 527 | 528 | // IsBinarySearchTree returns true, if the tree is a Binary Search 529 | // Tree (BST). 530 | func (n *Node[T]) IsBinarySearchTree(comparator ComparatorFunc[T]) bool { 531 | if n.IsLeafNode() { 532 | return true 533 | } 534 | 535 | // Use errNotBst to signal a condition to stop walking the 536 | // tree as soon as we know this is a not a BST. 537 | var last *Node[T] 538 | walkFunc := func(curr *Node[T]) error { 539 | if last != nil && comparator(last.Value, curr.Value) > 0 { 540 | return errNotBst 541 | } 542 | last = curr 543 | 544 | return nil 545 | } 546 | 547 | err := n.WalkInOrder(walkFunc) 548 | switch { 549 | case err == errNotBst: 550 | return false 551 | case err != nil: 552 | panic(err) 553 | default: 554 | return true 555 | } 556 | } 557 | 558 | // AddAttribute associates an attribute with the node, which will be 559 | // used when generating the Dot representation of the tree. 560 | func (n *Node[T]) AddAttribute(name, value string) { 561 | n.dotAttributes[name] = value 562 | } 563 | 564 | // GetDotAttributes returns the attributes associated with the node in 565 | // format suitable for using in the Dot representation. 566 | func (n *Node[T]) GetDotAttributes() string { 567 | attrs := "" 568 | for k, v := range n.dotAttributes { 569 | attrs += fmt.Sprintf("%s=%s ", k, v) 570 | } 571 | 572 | return strings.TrimRight(attrs, " ") 573 | } 574 | 575 | // dotId returns the unique node id, which is used when generating the 576 | // binary tree representation in Dot. 577 | func (n *Node[T]) dotId() int64 { 578 | addr := fmt.Sprintf("%p", n) 579 | id, err := strconv.ParseInt(addr[2:], 16, 64) 580 | if err != nil { 581 | panic(err) 582 | } 583 | 584 | return id 585 | } 586 | 587 | // WriteDot generates the Dot representation of the binary tree. 588 | func (n *Node[T]) WriteDot(w io.Writer) error { 589 | nodeAttrs := `[color=lightblue fillcolor=lightblue fontcolor=black shape=record style="filled, rounded"]` 590 | if _, err := fmt.Fprintln(w, "digraph {"); err != nil { 591 | return err 592 | } 593 | 594 | if _, err := fmt.Fprintf(w, "\tnode %s\n", nodeAttrs); err != nil { 595 | return err 596 | } 597 | 598 | walkFunc := func(n *Node[T]) error { 599 | nodeId := n.dotId() 600 | _, err := fmt.Fprintf(w, "\t%d [label=\"| %v|\" %s]\n", nodeId, n.Value, n.GetDotAttributes()) 601 | if err != nil { 602 | return err 603 | } 604 | 605 | if n.Left != nil { 606 | if _, err := fmt.Fprintf(w, "\t%d:l -> %d:v\n", nodeId, n.Left.dotId()); err != nil { 607 | return err 608 | } 609 | } 610 | 611 | if n.Right != nil { 612 | if _, err := fmt.Fprintf(w, "\t%d:r -> %d:v\n", nodeId, n.Right.dotId()); err != nil { 613 | return err 614 | } 615 | } 616 | 617 | return nil 618 | } 619 | 620 | if err := n.WalkPreOrder(walkFunc); err != nil { 621 | return err 622 | } 623 | 624 | if _, err := fmt.Fprintln(w, "}"); err != nil { 625 | return err 626 | } 627 | 628 | return nil 629 | } 630 | -------------------------------------------------------------------------------- /binarytree_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Marin Atanasov Nikolov 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // 1. Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer 9 | // in this position and unchanged. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) “AS IS” AND ANY EXPRESS OR 15 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | // IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | package binarytree_test 26 | 27 | import ( 28 | "bytes" 29 | "reflect" 30 | "strings" 31 | "testing" 32 | 33 | "gopkg.in/dnaeon/go-binarytree.v1" 34 | ) 35 | 36 | func TestHeightAndSize(t *testing.T) { 37 | // Our test tree 38 | // 39 | // __1 40 | // / \ 41 | // 2 3 42 | // / \ 43 | // 4 5 44 | // 45 | root := binarytree.NewNode(1) 46 | two := root.InsertLeft(2) 47 | root.InsertRight(3) 48 | two.InsertLeft(4) 49 | five := two.InsertRight(5) 50 | 51 | if root.Size() != 5 { 52 | t.Fatal("expected tree size should be 5") 53 | } 54 | 55 | if root.Height() != 2 { 56 | t.Fatal("expected height from root should be 2") 57 | } 58 | 59 | if two.Height() != 1 { 60 | t.Fatal("expected height from node (2) should be 1") 61 | } 62 | 63 | if five.Height() != 0 { 64 | t.Fatal("expected height from node (5) should be 0") 65 | } 66 | } 67 | 68 | func TestIsLeafNode(t *testing.T) { 69 | // Our test tree 70 | // 71 | // __1 72 | // / \ 73 | // 2 3 74 | // / \ 75 | // 4 5 76 | // 77 | root := binarytree.NewNode(1) 78 | two := root.InsertLeft(2) 79 | three := root.InsertRight(3) 80 | four := two.InsertLeft(4) 81 | five := two.InsertRight(5) 82 | 83 | if root.IsLeafNode() { 84 | t.Fatal("root node should not be a leaf") 85 | } 86 | 87 | if two.IsLeafNode() { 88 | t.Fatal("node (2) should not be a leaf") 89 | } 90 | 91 | if !three.IsLeafNode() { 92 | t.Fatal("node (3) should be a leaf") 93 | } 94 | 95 | if !four.IsLeafNode() { 96 | t.Fatal("node (4) should not be a leaf") 97 | } 98 | 99 | if !five.IsLeafNode() { 100 | t.Fatal("node (5) should be a leaf") 101 | } 102 | } 103 | 104 | func TestIsFullNode(t *testing.T) { 105 | // Our test tree 106 | // 107 | // __1 108 | // / \ 109 | // 2 3 110 | // / \ 111 | // 4 5 112 | // 113 | root := binarytree.NewNode(1) 114 | two := root.InsertLeft(2) 115 | three := root.InsertRight(3) 116 | four := two.InsertLeft(4) 117 | five := two.InsertRight(5) 118 | 119 | if !root.IsFullNode() { 120 | t.Fatal("root node should be full") 121 | } 122 | 123 | if !two.IsFullNode() { 124 | t.Fatal("node (2) should be full") 125 | } 126 | 127 | if three.IsFullNode() { 128 | t.Fatal("node (3) should not be full") 129 | } 130 | 131 | if four.IsFullNode() { 132 | t.Fatal("node (4) should not be full") 133 | } 134 | 135 | if five.IsFullNode() { 136 | t.Fatal("node (5) should not be full") 137 | } 138 | } 139 | 140 | func TestWalkInOrder(t *testing.T) { 141 | // Our test tree 142 | // 143 | // __1 144 | // / \ 145 | // 2 3 146 | // / \ 147 | // 4 5 148 | // 149 | root := binarytree.NewNode(1) 150 | two := root.InsertLeft(2) 151 | root.InsertRight(3) 152 | two.InsertLeft(4) 153 | two.InsertRight(5) 154 | 155 | result := make([]int, 0) 156 | wantResult := []int{4, 2, 5, 1, 3} 157 | walkFunc := func(node *binarytree.Node[int]) error { 158 | result = append(result, node.Value) 159 | return nil 160 | } 161 | 162 | if err := root.WalkInOrder(walkFunc); err != nil { 163 | t.Fatal(err) 164 | } 165 | 166 | if !reflect.DeepEqual(result, wantResult) { 167 | t.Fatalf("want in-order values %v, got %v", wantResult, result) 168 | } 169 | } 170 | 171 | func TestWalkPreOrder(t *testing.T) { 172 | // Our test tree 173 | // 174 | // __1 175 | // / \ 176 | // 2 3 177 | // / \ 178 | // 4 5 179 | // 180 | root := binarytree.NewNode(1) 181 | two := root.InsertLeft(2) 182 | root.InsertRight(3) 183 | two.InsertLeft(4) 184 | two.InsertRight(5) 185 | 186 | result := make([]int, 0) 187 | wantResult := []int{1, 2, 4, 5, 3} 188 | walkFunc := func(node *binarytree.Node[int]) error { 189 | result = append(result, node.Value) 190 | return nil 191 | } 192 | 193 | if err := root.WalkPreOrder(walkFunc); err != nil { 194 | t.Fatal(err) 195 | } 196 | 197 | if !reflect.DeepEqual(wantResult, result) { 198 | t.Fatalf("want pre-order values %v, got %v", wantResult, result) 199 | } 200 | } 201 | 202 | func TestWalkPostOrder(t *testing.T) { 203 | // Our test tree 204 | // 205 | // __1 206 | // / \ 207 | // 2 3 208 | // / \ 209 | // 4 5 210 | // 211 | root := binarytree.NewNode(1) 212 | two := root.InsertLeft(2) 213 | root.InsertRight(3) 214 | two.InsertLeft(4) 215 | two.InsertRight(5) 216 | 217 | result := make([]int, 0) 218 | wantResult := []int{4, 5, 2, 3, 1} 219 | walkFunc := func(node *binarytree.Node[int]) error { 220 | result = append(result, node.Value) 221 | return nil 222 | } 223 | 224 | if err := root.WalkPostOrder(walkFunc); err != nil { 225 | t.Fatal(err) 226 | } 227 | 228 | if !reflect.DeepEqual(wantResult, result) { 229 | t.Fatalf("want post-order values %v, got %v", wantResult, result) 230 | } 231 | } 232 | 233 | func TestWalkLevelOrder(t *testing.T) { 234 | // Our test tree 235 | // 236 | // __1 237 | // / \ 238 | // 2 3 239 | // / \ 240 | // 4 5 241 | // 242 | root := binarytree.NewNode(1) 243 | two := root.InsertLeft(2) 244 | root.InsertRight(3) 245 | two.InsertLeft(4) 246 | two.InsertRight(5) 247 | 248 | result := make([]int, 0) 249 | wantResult := []int{1, 2, 3, 4, 5} 250 | walkFunc := func(node *binarytree.Node[int]) error { 251 | result = append(result, node.Value) 252 | return nil 253 | } 254 | 255 | if err := root.WalkLevelOrder(walkFunc); err != nil { 256 | t.Fatal(err) 257 | } 258 | 259 | if !reflect.DeepEqual(wantResult, result) { 260 | t.Fatalf("want level-order values %v, got %v", wantResult, result) 261 | } 262 | } 263 | 264 | func TestSkipNodeHandlers(t *testing.T) { 265 | // Construct the following simple binary tree 266 | // 267 | // __1 268 | // / \ 269 | // 2 3 270 | // / \ 271 | // 4 5 272 | // 273 | root := binarytree.NewNode(1) 274 | two := root.InsertLeft(2) 275 | root.InsertRight(3) 276 | two.InsertLeft(4) 277 | two.InsertRight(5) 278 | 279 | skipFunc := func(n *binarytree.Node[int]) bool { 280 | // Skip the sub-tree at node (2) 281 | if n.Value == 2 { 282 | return true 283 | } 284 | 285 | return false 286 | } 287 | 288 | values := make([]int, 0) 289 | walkFunc := func(n *binarytree.Node[int]) error { 290 | values = append(values, n.Value) 291 | return nil 292 | } 293 | 294 | root.AddSkipNodeFunc(skipFunc) 295 | if err := root.WalkInOrder(walkFunc); err != nil { 296 | t.Fatal(err) 297 | } 298 | 299 | wantValues := []int{1, 3} 300 | if !reflect.DeepEqual(values, wantValues) { 301 | t.Fatalf("want in-order values %v, got %v", wantValues, values) 302 | } 303 | } 304 | 305 | func TestFindNode(t *testing.T) { 306 | // Construct the following simple binary tree 307 | // 308 | // __1 309 | // / \ 310 | // 2 3 311 | // / \ 312 | // 4 5 313 | // 314 | root := binarytree.NewNode(1) 315 | two := root.InsertLeft(2) 316 | root.InsertRight(3) 317 | two.InsertLeft(4) 318 | two.InsertRight(5) 319 | 320 | goodPredicate := func(n *binarytree.Node[int]) bool { 321 | if n.Value == 2 { 322 | return true 323 | } 324 | return false 325 | } 326 | 327 | node, ok := root.FindNode(goodPredicate) 328 | if !ok { 329 | t.Fatal("unable to find node (2)") 330 | } 331 | 332 | // The node we are looking for should have left and right 333 | // children 334 | if node.Left == nil || node.Right == nil { 335 | t.Fatal("node (2) does not have left or right children") 336 | } 337 | 338 | // No node will match is supposed to match with this predicate 339 | badPredicate := func(n *binarytree.Node[int]) bool { 340 | return false 341 | } 342 | 343 | if _, ok := root.FindNode(badPredicate); ok { 344 | t.Fatal("no node is supposed to match the predicate") 345 | } 346 | } 347 | 348 | func TestIsFullTree(t *testing.T) { 349 | // Our test tree 350 | // 351 | // __1 352 | // / \ 353 | // 2 3 354 | // / \ 355 | // 4 5 356 | root := binarytree.NewNode(1) 357 | two := root.InsertLeft(2) 358 | root.InsertRight(3) 359 | two.InsertLeft(4) 360 | two.InsertRight(5) 361 | 362 | if !root.IsFullTree() { 363 | t.Fatal("tree should be full") 364 | } 365 | } 366 | 367 | func TestIsNotFullTree(t *testing.T) { 368 | // Our test tree 369 | // 370 | // __1 371 | // / 372 | // 2 373 | // / \ 374 | // 4 5 375 | // 376 | root := binarytree.NewNode(1) 377 | two := root.InsertLeft(2) 378 | two.InsertLeft(4) 379 | two.InsertRight(5) 380 | 381 | if root.IsFullTree() { 382 | t.Fatal("tree should not be full") 383 | } 384 | } 385 | 386 | func TestTreeIsDegenerateTree(t *testing.T) { 387 | // Our test tree 388 | // 389 | // 1 390 | // / 391 | // 2 392 | // \ 393 | // 3 394 | // / 395 | // 4 396 | // 397 | root := binarytree.NewNode(1) 398 | two := root.InsertLeft(2) 399 | three := two.InsertRight(3) 400 | three.InsertLeft(4) 401 | 402 | if !root.IsDegenerateTree() { 403 | t.Fatal("tree should be degenerate") 404 | } 405 | } 406 | 407 | func TestTreeIsNotDegenerate(t *testing.T) { 408 | // Our test tree 409 | // 410 | // __1 411 | // / 412 | // 2 413 | // / \ 414 | // 4 5 415 | // 416 | root := binarytree.NewNode(1) 417 | two := root.InsertLeft(2) 418 | two.InsertLeft(4) 419 | two.InsertRight(5) 420 | 421 | if root.IsDegenerateTree() { 422 | t.Fatal("tree should not be degenerate") 423 | } 424 | } 425 | 426 | func TestIsBalancedTree(t *testing.T) { 427 | // Unbalanced tree 428 | // 429 | // 1 430 | // / 431 | // 2 432 | // / 433 | // 3 434 | // 435 | unbalanced_root := binarytree.NewNode(1) 436 | two := unbalanced_root.InsertLeft(2) 437 | two.InsertLeft(3) 438 | 439 | if unbalanced_root.IsBalancedTree() { 440 | t.Fatal("tree should not be balanced") 441 | } 442 | 443 | // Another unbalanced tree 444 | // 445 | // __1 446 | // / \ 447 | // __2 3 448 | // / \ 449 | // 4 5 450 | // / \ 451 | // 6 7 452 | unbalanced_root = binarytree.NewNode(1) 453 | unbalanced_root.InsertRight(3) 454 | two = unbalanced_root.InsertLeft(2) 455 | two.InsertRight(5) 456 | four := two.InsertLeft(4) 457 | four.InsertLeft(6) 458 | four.InsertRight(7) 459 | 460 | if unbalanced_root.IsBalancedTree() { 461 | t.Fatal("tree should not be balanced") 462 | } 463 | 464 | // A single root node is a balanced tree 465 | leaf := binarytree.NewNode(1) 466 | if !leaf.IsBalancedTree() { 467 | t.Fatal("single leaf node should be balanced") 468 | } 469 | 470 | // Yet another unbalanced tree 471 | // 472 | // __1 473 | // / 474 | // 2 475 | // / \ 476 | // 4 5 477 | // 478 | unbalanced_root = binarytree.NewNode(1) 479 | two = unbalanced_root.InsertLeft(2) 480 | two.InsertLeft(4) 481 | two.InsertRight(5) 482 | 483 | if unbalanced_root.IsBalancedTree() { 484 | t.Fatal("tree should not be balanced") 485 | } 486 | 487 | // The sub-tree with root node (2) is balanced 488 | if !two.IsBalancedTree() { 489 | t.Fatal("tree with root (2) should be balanced") 490 | } 491 | 492 | // A balanced tree 493 | // 494 | // 1__ 495 | // / \ 496 | // 2 3 497 | // / \ 498 | // 4 5 499 | balanced_root := binarytree.NewNode(1) 500 | balanced_root.InsertLeft(2) 501 | three := balanced_root.InsertRight(3) 502 | three.InsertLeft(4) 503 | three.InsertRight(5) 504 | 505 | if !balanced_root.IsBalancedTree() { 506 | t.Fatal("tree should be balanced") 507 | } 508 | } 509 | 510 | func TestIsCompleteTree(t *testing.T) { 511 | // A complete binary tree 512 | // 513 | // 1 514 | // / \ 515 | // 2 3 516 | // 517 | root := binarytree.NewNode(1) 518 | root.InsertLeft(2) 519 | root.InsertRight(3) 520 | 521 | if !root.IsCompleteTree() { 522 | t.Fatal("tree should be complete") 523 | } 524 | 525 | // A complete binary tree 526 | // 527 | // 1 528 | // / \ 529 | // 2 3 530 | // / 531 | // 4 532 | // 533 | root = binarytree.NewNode(1) 534 | root.InsertRight(3) 535 | two := root.InsertLeft(2) 536 | two.InsertLeft(4) 537 | 538 | if !root.IsCompleteTree() { 539 | t.Fatal("tree should be complete") 540 | } 541 | 542 | // A complete binary tree 543 | // 544 | // __1__ 545 | // / \ 546 | // 2 3 547 | // / \ / 548 | // 4 5 6 549 | // 550 | root = binarytree.NewNode(1) 551 | two = root.InsertLeft(2) 552 | two.InsertLeft(4) 553 | two.InsertRight(5) 554 | three := root.InsertRight(3) 555 | three.InsertLeft(6) 556 | 557 | if !root.IsCompleteTree() { 558 | t.Fatal("tree should be complete") 559 | } 560 | 561 | // Not complete binary tree 562 | // 563 | // __1_ 564 | // / \ 565 | // 2 3 566 | // / \ \ 567 | // 4 5 6 568 | // 569 | root = binarytree.NewNode(1) 570 | two = root.InsertLeft(2) 571 | two.InsertLeft(4) 572 | two.InsertRight(5) 573 | three = root.InsertRight(3) 574 | three.InsertRight(6) 575 | 576 | if root.IsCompleteTree() { 577 | t.Fatal("tree should not be complete") 578 | } 579 | 580 | // Not complete binary tree 581 | // 582 | // 1 583 | // / 584 | // 2 585 | // / 586 | // 3 587 | // 588 | root = binarytree.NewNode(1) 589 | two = root.InsertLeft(2) 590 | two.InsertLeft(3) 591 | 592 | if root.IsCompleteTree() { 593 | t.Fatal("tree should not be complete") 594 | } 595 | 596 | // Not complete binary tree 597 | // 598 | // __1__ 599 | // / \ 600 | // 2 3 601 | // \ / \ 602 | // 4 5 6 603 | root = binarytree.NewNode(1) 604 | two = root.InsertLeft(2) 605 | two.InsertRight(4) 606 | three = root.InsertRight(3) 607 | three.InsertLeft(5) 608 | three.InsertRight(6) 609 | 610 | if root.IsCompleteTree() { 611 | t.Fatal("tree should not be complete") 612 | } 613 | 614 | // Not complete binary tree 615 | // 616 | // 1__ 617 | // / \ 618 | // 2 3 619 | // / \ 620 | // 4 5 621 | root = binarytree.NewNode(1) 622 | root.InsertLeft(2) 623 | three = root.InsertRight(3) 624 | three.InsertLeft(4) 625 | three.InsertRight(5) 626 | 627 | if root.IsCompleteTree() { 628 | t.Fatal("tree should not be complete") 629 | } 630 | 631 | // A complete binary tree with a single root node 632 | root = binarytree.NewNode(1) 633 | if !root.IsCompleteTree() { 634 | t.Fatal("tree should be complete") 635 | } 636 | } 637 | 638 | func TestIsPerfectTree(t *testing.T) { 639 | // A perfect binary tree 640 | // 641 | // 1 642 | // / \ 643 | // 2 3 644 | // 645 | root := binarytree.NewNode(1) 646 | root.InsertLeft(2) 647 | root.InsertRight(3) 648 | 649 | if !root.IsPerfectTree() { 650 | t.Fatal("tree should be perfect") 651 | } 652 | 653 | // A non-perfect binary tree 654 | // 655 | // 1 656 | // / \ 657 | // 2 3 658 | // / 659 | // 4 660 | // 661 | root = binarytree.NewNode(1) 662 | root.InsertRight(3) 663 | two := root.InsertLeft(2) 664 | two.InsertLeft(4) 665 | 666 | if root.IsPerfectTree() { 667 | t.Fatal("tree should not be perfect") 668 | } 669 | 670 | // A non-perfect binary tree 671 | // 672 | // __1__ 673 | // / \ 674 | // 2 3 675 | // / \ / 676 | // 4 5 6 677 | // 678 | root = binarytree.NewNode(1) 679 | two = root.InsertLeft(2) 680 | two.InsertLeft(4) 681 | two.InsertRight(5) 682 | three := root.InsertRight(3) 683 | three.InsertLeft(6) 684 | 685 | if root.IsPerfectTree() { 686 | t.Fatal("tree should not be perfect") 687 | } 688 | 689 | // A perfect binary tree 690 | // 691 | // __1__ 692 | // / \ 693 | // 2 3 694 | // / \ / \ 695 | // 4 5 6 7 696 | // 697 | root = binarytree.NewNode(1) 698 | two = root.InsertLeft(2) 699 | two.InsertLeft(4) 700 | two.InsertRight(5) 701 | three = root.InsertRight(3) 702 | three.InsertLeft(6) 703 | three.InsertRight(7) 704 | 705 | if !root.IsPerfectTree() { 706 | t.Fatal("tree should be perfect") 707 | } 708 | 709 | // A non-perfect binary tree 710 | // 711 | // 1 712 | // / 713 | // 2 714 | // / 715 | // 3 716 | // 717 | root = binarytree.NewNode(1) 718 | two = root.InsertLeft(2) 719 | two.InsertLeft(3) 720 | 721 | if root.IsPerfectTree() { 722 | t.Fatal("tree should not be perfect") 723 | } 724 | 725 | // A non-perfect binary tree 726 | // 727 | // 1__ 728 | // / \ 729 | // 2 3 730 | // / \ 731 | // 4 5 732 | root = binarytree.NewNode(1) 733 | root.InsertLeft(2) 734 | three = root.InsertRight(3) 735 | three.InsertLeft(4) 736 | three.InsertRight(5) 737 | 738 | if root.IsPerfectTree() { 739 | t.Fatal("tree should not be perfect") 740 | } 741 | 742 | // A perfect binary tree with a single root node 743 | root = binarytree.NewNode(1) 744 | if !root.IsPerfectTree() { 745 | t.Fatal("tree should be perfect") 746 | } 747 | } 748 | 749 | func TestIsBinarySearchTree(t *testing.T) { 750 | // A valid BST 751 | // 752 | // 2 753 | // / \ 754 | // 1 3 755 | // 756 | root := binarytree.NewNode(2) 757 | root.InsertLeft(1) 758 | root.InsertRight(3) 759 | 760 | if !root.IsBinarySearchTree(binarytree.IntComparator) { 761 | t.Fatal("tree should be BST") 762 | } 763 | 764 | // Invalid BST 765 | // 766 | // 1 767 | // / \ 768 | // 2 3 769 | // 770 | root = binarytree.NewNode(1) 771 | root.InsertLeft(2) 772 | root.InsertRight(3) 773 | 774 | if root.IsBinarySearchTree(binarytree.IntComparator) { 775 | t.Fatal("tree should not be BST") 776 | } 777 | 778 | // Invalid BST 779 | // 780 | // 1 781 | // / \ 782 | // 2 3 783 | // / 784 | // 4 785 | // 786 | root = binarytree.NewNode(1) 787 | root.InsertRight(3) 788 | two := root.InsertLeft(2) 789 | two.InsertLeft(4) 790 | 791 | if root.IsBinarySearchTree(binarytree.IntComparator) { 792 | t.Fatal("tree should not be BST") 793 | } 794 | 795 | // A valid BST 796 | // 797 | // ______8 798 | // / \ 799 | // 3__ 10___ 800 | // / \ \ 801 | // 1 6 _14 802 | // / \ / 803 | // 4 7 13 804 | // 805 | root = binarytree.NewNode(8) 806 | three := root.InsertLeft(3) 807 | three.InsertLeft(1) 808 | six := three.InsertRight(6) 809 | six.InsertLeft(4) 810 | six.InsertRight(7) 811 | ten := root.InsertRight(10) 812 | fourteen := ten.InsertRight(14) 813 | fourteen.InsertLeft(13) 814 | 815 | if !root.IsBinarySearchTree(binarytree.IntComparator) { 816 | t.Fatal("tree should be BST") 817 | } 818 | 819 | // A tree with a single root node is a valid BST 820 | root = binarytree.NewNode(1) 821 | if !root.IsBinarySearchTree(binarytree.IntComparator) { 822 | t.Fatal("tree should be BST") 823 | } 824 | 825 | // A valid BST 826 | // 827 | // B 828 | // / \ 829 | // A C 830 | // 831 | str_root := binarytree.NewNode("B") 832 | str_root.InsertLeft("A") 833 | str_root.InsertRight("C") 834 | 835 | if !str_root.IsBinarySearchTree(binarytree.StringComparator) { 836 | t.Fatal("tree should be BST") 837 | } 838 | 839 | // Invalid BST 840 | // 841 | // A 842 | // / \ 843 | // B C 844 | // / 845 | // D 846 | str_root = binarytree.NewNode("A") 847 | str_root.InsertRight("C") 848 | b := str_root.InsertLeft("B") 849 | b.InsertLeft("D") 850 | 851 | if str_root.IsBinarySearchTree(binarytree.StringComparator) { 852 | t.Fatal("tree should not be BST") 853 | } 854 | } 855 | 856 | func TestNodeAttributes(t *testing.T) { 857 | root := binarytree.NewNode(1) 858 | 859 | if root.GetDotAttributes() != "" { 860 | t.Fatal("node is expected to have no attributes") 861 | } 862 | 863 | root.AddAttribute("color", "green") 864 | 865 | wantAttrs := "color=green" 866 | gotAttrs := root.GetDotAttributes() 867 | if gotAttrs != wantAttrs { 868 | t.Fatal("node attributes mismatch") 869 | } 870 | } 871 | 872 | func TestWriteDot(t *testing.T) { 873 | // Our test tree 874 | // 875 | // 1__ 876 | // / \ 877 | // 2 3 878 | // / \ 879 | // 4 5 880 | root := binarytree.NewNode(1) 881 | root.InsertLeft(2) 882 | three := root.InsertRight(3) 883 | three.InsertLeft(4) 884 | three.InsertRight(5) 885 | 886 | var buf bytes.Buffer 887 | if err := root.WriteDot(&buf); err != nil { 888 | t.Fatal(err) 889 | } 890 | 891 | output := buf.String() 892 | if output == "" { 893 | t.Fatal("got empty dot representation") 894 | } 895 | 896 | if !strings.HasPrefix(output, "digraph {") { 897 | t.Fatal("missing dot prefix") 898 | } 899 | 900 | if !strings.HasSuffix(output, "}\n") { 901 | t.Fatal("missing dot suffix") 902 | } 903 | } 904 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module gopkg.in/dnaeon/go-binarytree.v1 2 | 3 | go 1.19 4 | 5 | require gopkg.in/dnaeon/go-deque.v1 v1.0.0-20220924123127-c8d2565cae45 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | gopkg.in/dnaeon/go-deque.v1 v1.0.0-20220924123127-c8d2565cae45 h1:vqOCxIOG4p9d4FzsnkzYUhKYVRnykSPdBk46of4bS6g= 2 | gopkg.in/dnaeon/go-deque.v1 v1.0.0-20220924123127-c8d2565cae45/go.mod h1:/oUlvC7v/6StKZYUKPFtH7YbgkLITXvh4CMF7cElPo8= 3 | -------------------------------------------------------------------------------- /images/binarytree.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 824634802240 14 | 15 | 16 | 17 | 10 18 | 19 | 20 | 21 | 22 | 23 | 824634802304 24 | 25 | 26 | 27 | 5 28 | 29 | 30 | 31 | 32 | 33 | 824634802240:l->824634802304:v 34 | 35 | 36 | 37 | 38 | 39 | 824634802368 40 | 41 | 42 | 43 | 20 44 | 45 | 46 | 47 | 48 | 49 | 824634802240:r->824634802368:v 50 | 51 | 52 | 53 | 54 | 55 | 824634802432 56 | 57 | 58 | 59 | 9 60 | 61 | 62 | 63 | 64 | 65 | 824634802304:l->824634802432:v 66 | 67 | 68 | 69 | 70 | 71 | 824634802496 72 | 73 | 74 | 75 | 18 76 | 77 | 78 | 79 | 80 | 81 | 824634802304:r->824634802496:v 82 | 83 | 84 | 85 | 86 | 87 | 824634802560 88 | 89 | 90 | 91 | 3 92 | 93 | 94 | 95 | 96 | 97 | 824634802368:l->824634802560:v 98 | 99 | 100 | 101 | 102 | 103 | 824634802624 104 | 105 | 106 | 107 | 7 108 | 109 | 110 | 111 | 112 | 113 | 824634802368:r->824634802624:v 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /vendor/gopkg.in/dnaeon/go-deque.v1/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Emacs backup files 2 | *~ 3 | -------------------------------------------------------------------------------- /vendor/gopkg.in/dnaeon/go-deque.v1/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Marin Atanasov Nikolov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer 10 | in this position and unchanged. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /vendor/gopkg.in/dnaeon/go-deque.v1/Makefile: -------------------------------------------------------------------------------- 1 | get: 2 | go get -v -t -d ./... 3 | 4 | test: 5 | go test -v -race ./... 6 | 7 | test_cover: 8 | go test -v -race -coverprofile=coverage.txt -covermode=atomic ./... 9 | 10 | .PHONY: get test test_cover 11 | -------------------------------------------------------------------------------- /vendor/gopkg.in/dnaeon/go-deque.v1/README.org: -------------------------------------------------------------------------------- 1 | * go-deque 2 | 3 | A simple, generic, thread-safe implementation of [[https://en.wikipedia.org/wiki/Double-ended_queue][double-ended queue]] in 4 | Go. 5 | 6 | ** Installation 7 | 8 | Install =go-deque= by executing the following command. 9 | 10 | #+begin_src shell 11 | go get -v gopkg.in/dnaeon/go-deque.v1 12 | #+end_src 13 | 14 | ** Usage 15 | 16 | See the included [[file:deque_test.go][test cases]] for some examples. 17 | 18 | ** Tests 19 | 20 | Run the tests. 21 | 22 | #+begin_src shell 23 | make test 24 | #+end_src 25 | 26 | ** License 27 | 28 | =go-deque= is Open Source and licensed under the [[http://opensource.org/licenses/BSD-2-Clause][BSD License]]. 29 | -------------------------------------------------------------------------------- /vendor/gopkg.in/dnaeon/go-deque.v1/deque.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Marin Atanasov Nikolov 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // 1. Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer 9 | // in this position and unchanged. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | // IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | package deque 26 | 27 | import ( 28 | "errors" 29 | "sync" 30 | ) 31 | 32 | // ErrEmptyQueue is an error which is returned when attempting to pop 33 | // an item from an empty queue 34 | var ErrEmptyQueue = errors.New("Queue is empty") 35 | 36 | type Deque[T any] struct { 37 | sync.RWMutex 38 | items []T 39 | } 40 | 41 | // New creates a new deque 42 | func New[T any]() *Deque[T] { 43 | d := &Deque[T]{ 44 | items: make([]T, 0), 45 | } 46 | 47 | return d 48 | } 49 | 50 | // PushBack inserts a new item at the back 51 | func (d *Deque[T]) PushBack(value T) { 52 | d.Lock() 53 | defer d.Unlock() 54 | d.items = append(d.items, value) 55 | } 56 | 57 | // PushFront inserts a new item at the front 58 | func (d *Deque[T]) PushFront(value T) { 59 | d.Lock() 60 | defer d.Unlock() 61 | d.items = append([]T{value}, d.items...) 62 | } 63 | 64 | // IsEmpty returns true if the deque is empty, false otherwise 65 | func (d *Deque[T]) IsEmpty() bool { 66 | d.RLock() 67 | defer d.RUnlock() 68 | return len(d.items) == 0 69 | } 70 | 71 | // PopBack pops an item from the back 72 | func (d *Deque[T]) PopBack() (T, error) { 73 | var empty T 74 | if d.IsEmpty() { 75 | return empty, ErrEmptyQueue 76 | } 77 | 78 | d.Lock() 79 | defer d.Unlock() 80 | 81 | size := len(d.items) 82 | item := d.items[size-1] 83 | d.items = d.items[:size-1] 84 | 85 | return item, nil 86 | } 87 | 88 | // PopFront pops an item from the front 89 | func (d *Deque[T]) PopFront() (T, error) { 90 | var empty T 91 | if d.IsEmpty() { 92 | return empty, ErrEmptyQueue 93 | } 94 | 95 | d.Lock() 96 | defer d.Unlock() 97 | 98 | item := d.items[0] 99 | d.items = d.items[1:] 100 | 101 | return item, nil 102 | } 103 | 104 | // Length returns the size of the queue 105 | func (d *Deque[T]) Length() int { 106 | d.RLock() 107 | defer d.RUnlock() 108 | return len(d.items) 109 | } 110 | 111 | // PeekFront peeks at the front 112 | func (d *Deque[T]) PeekFront() (T, error) { 113 | var empty T 114 | if d.IsEmpty() { 115 | return empty, ErrEmptyQueue 116 | } 117 | 118 | d.RLock() 119 | defer d.RUnlock() 120 | 121 | return d.items[0], nil 122 | } 123 | 124 | // PeekBack peeks at the back 125 | func (d *Deque[T]) PeekBack() (T, error) { 126 | var empty T 127 | if d.IsEmpty() { 128 | return empty, ErrEmptyQueue 129 | } 130 | 131 | d.RLock() 132 | defer d.RUnlock() 133 | 134 | size := len(d.items) 135 | return d.items[size-1], nil 136 | } 137 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # gopkg.in/dnaeon/go-deque.v1 v1.0.0-20220924123127-c8d2565cae45 2 | ## explicit; go 1.19 3 | gopkg.in/dnaeon/go-deque.v1 4 | --------------------------------------------------------------------------------