├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── .npmrc ├── .travis.yml ├── code-of-conduct.md ├── contributing.md ├── index.js ├── license.md ├── package.json ├── readme.md ├── src ├── node.js └── tree.js ├── test ├── empty.js ├── multiple.js ├── node.js ├── single.js └── types │ ├── avlbinstree.ts │ └── tsconfig.json └── types └── avlbinstree.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [{*.json, *.yml}] 15 | indent_style = space 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior. 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **Screenshots** 17 | If applicable, add screenshots to help explain your problem. 18 | 19 | **Technical Info (please complete the following information)** 20 | - OS: 21 | - Avlbinstree Version: 22 | - Node.js Version: 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Additional context** 14 | Add any other context or screenshots about the feature request here. 15 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | yarn.lock 4 | 5 | # logs 6 | *.log 7 | 8 | # OS 9 | .DS_Store 10 | 11 | # IDE 12 | .vscode 13 | .idea 14 | *.swp 15 | *.swo 16 | 17 | # coverage 18 | .nyc_output 19 | coverage 20 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 12 4 | - 10 5 | - 8 6 | before_install: 7 | - npm install --global npm@6.8.0 8 | - npm --version 9 | after_success: npm run coveralls 10 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at klaussinani@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Avlbinstree 2 | 3 | Thank you for taking the time to contribute to Avlbinstree! 4 | 5 | Please note that this project is released with a [Contributor Code of Conduct](code-of-conduct.md). By participating in this project you agree to abide by its terms. 6 | 7 | ## How to contribute 8 | 9 | ### Improve documentation 10 | 11 | Typo corrections, error fixes, better explanations, more examples etc. Open an issue regarding anything that you think it could be improved. You can use the [`docs` label](https://github.com/klaussinani/avlbinstree/labels/docs) to find out what others have suggested. 12 | 13 | ### Improve issues 14 | 15 | Sometimes reported issues lack information, are not reproducible, or are even plain invalid. Help us out to make them easier to resolve. Handling issues takes a lot of time that we could rather spend on fixing bugs and adding features. 16 | 17 | ### Give feedback on issues 18 | 19 | We're always looking for more opinions on discussions in the issue tracker. It's a good opportunity to influence the future direction of the project. 20 | 21 | The [`question` label](https://github.com/klaussinani/avlbinstree/labels/question) is a good place to find ongoing discussions. 22 | 23 | ### Write code 24 | 25 | You can use issue labels to discover issues you could help us out with. 26 | 27 | - [`feature request` issues](https://github.com/klaussinani/avlbinstree/labels/feature%20request) are features we are open to including. 28 | - [`bug` issues](https://github.com/klaussinani/avlbinstree/labels/bug) are known bugs we would like to fix. 29 | - [`future` issues](https://github.com/klaussinani/avlbinstree/labels/future) are those that we'd like to get to, but not anytime soon. Please check before working on these since we may not yet want to take on the burden of supporting those features. 30 | - on the [`help wanted`](https://github.com/klaussinani/avlbinstree/labels/help%20wanted) label you can always find something exciting going on. 31 | 32 | You may find an issue is assigned, or has the [`assigned` label](https://github.com/klaussinani/avlbinstree/labels/assigned). Please double-check before starting on this issue because somebody else is likely already working on it. 33 | 34 | ### Translating Documentation 35 | 36 | #### Create a Translation 37 | 38 | - Ensure that the document is not already translated in your target language. 39 | - Add the name of the language to the document as an extension, e.g: `readme.JP.md` 40 | - Create a Pull Request including the language in the title, e.g: `Readme: Japanese Translation` 41 | 42 | ### Submitting an issue 43 | 44 | - Search the issue tracker before opening an issue 45 | - Ensure you're using the latest version of Avlbinstree 46 | - Use a descriptive title 47 | - Include as much information as possible; 48 | - Steps to reproduce the issue 49 | - Error message 50 | - Avlbinstree version 51 | - Operating system **etc** 52 | 53 | ### Submitting a pull request 54 | 55 | - Non-trivial changes are often best discussed in an issue first, to prevent you from doing unnecessary work 56 | - Try making the pull request from a [topic branch](https://github.com/dchelimsky/rspec/wiki/Topic-Branches) if it is of crucial importance 57 | - Use a descriptive title for the pull request and commits 58 | - You might be asked to do changes to your pull request, you can do that by just [updating the existing one](https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md) 59 | 60 | > Based on project [AVA](https://github.com/avajs/ava/blob/master/contributing.md)'s contributing.md 61 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Node = require('./src/node'); 3 | const Tree = require('./src/tree'); 4 | 5 | module.exports = Object.assign({}, {Node}, {Tree}); 6 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 - present Klaus Sinani (klaussinani.github.io) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "avlbinstree", 3 | "version": "1.0.0", 4 | "description": "AVL self-balancing binary search trees for ES6", 5 | "license": "MIT", 6 | "repository": "klaussinani/avlbinstree", 7 | "author": { 8 | "name": "Klaus Sinani", 9 | "email": "klaussinani@gmail.com", 10 | "url": "https://klaussinani.github.io" 11 | }, 12 | "engines": { 13 | "node": ">=6" 14 | }, 15 | "types": "types/avlbinstree.d.ts", 16 | "files": [ 17 | "src", 18 | "types", 19 | "index.js" 20 | ], 21 | "keywords": [ 22 | "avl", 23 | "self", 24 | "balancing", 25 | "binary", 26 | "search", 27 | "tree", 28 | "es6", 29 | "data", 30 | "structure", 31 | "typescript" 32 | ], 33 | "scripts": { 34 | "lint": "xo", 35 | "test:ts": "tsc --noEmit -p test/types", 36 | "test:js": "npm run lint && nyc ava", 37 | "test": "npm run test:ts && npm run test:js", 38 | "coveralls": "nyc report --reporter=text-lcov | coveralls" 39 | }, 40 | "devDependencies": { 41 | "ava": "2.3.0", 42 | "coveralls": "^3.0.3", 43 | "nyc": "^13.3.0", 44 | "typescript": "^3.5.3", 45 | "xo": "^0.24.0" 46 | }, 47 | "xo": { 48 | "space": 2 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | Avlbinstree 3 |

4 | 5 |

6 | AVL self-balancing binary search trees for ES6 7 |

8 | 9 |

10 | 11 | Build Status 12 | 13 | 14 | Coverage Status 15 | 16 |

17 | 18 | ## Description 19 | 20 | ES6 implementation of the AVL self-balancing binary search tree data structure with TypeScript support. 21 | 22 | Visit the [contributing guidelines](https://github.com/klaussinani/avlbinstree/blob/master/contributing.md#translating-documentation) to learn more on how to translate this document into more languages. 23 | 24 | ## Contents 25 | 26 | - [Description](#description) 27 | - [Install](#install) 28 | - [In Depth](#in-depth) 29 | - [Usage](#usage) 30 | - [API](#api) 31 | - [Development](#development) 32 | - [Related](#related) 33 | - [Team](#team) 34 | - [License](#license) 35 | 36 | ## Install 37 | 38 | ### Yarn 39 | 40 | ```bash 41 | yarn add avlbinstree 42 | ``` 43 | 44 | ### NPM 45 | 46 | ```bash 47 | npm install avlbinstree 48 | ``` 49 | 50 | ## In Depth 51 | 52 | An AVL tree is a self-balancing binary search tree data structure, whose nodes contain a unique `key`, an associated `value`, and point to two distinguished `left` and `right` sub-trees. In the tree, the heights of the two child sub-trees of any node differ by at most one. If during a mutating operation, e.g insertion, deletion, a temporary height difference of more than one arises between two child sub-trees, the balance property of the parent sub-tree, thus of the entire tree itself, is restored through the internal usage of tree rotations. These repair tools move the tree nodes only `vertically`, so that the `horizontal/in-order` sequence of their keys is fully preserved. Lookup, insertion, and deletion all take `O(log n)` time in both the average and worst cases, where `n` is the number of nodes in the tree prior to the operation. Insertions and deletions may require the tree to be rebalanced by one or more tree rotations. 53 | 54 | ## Usage 55 | 56 | Avlbinstree exposes a chainable API, that can be utilized through a simple and minimal syntax, allowing you to combine methods effectively. 57 | 58 | Usage examples can be also found at the [`test`](https://github.com/klaussinani/avlbinstree/tree/master/test) directory. 59 | 60 | ```js 61 | 'use strict'; 62 | const {Tree, Node} = require('avlbinstree'); 63 | 64 | const tree = new Tree(); 65 | //=> Tree { root: null } 66 | 67 | tree.insert(9, 'A'); 68 | // => Tree { root: Node { left: null, right: null, key: 9, value: 'A' } } 69 | 70 | tree.root; 71 | //=> Node { left: null, right: null, key: 10, value: 'A' } 72 | 73 | const node = new Node(9, 'A'); 74 | 75 | tree.root.key === node.key; 76 | //=> true 77 | 78 | tree.root.value === node.value; 79 | //=> true 80 | 81 | tree.insert(5, 'B').insert(13, 'C').root; 82 | //=> Node { left: [Node], right: [Node], key: 9, value: 'A' } 83 | 84 | tree.root.left; 85 | //=> Node { left: null, right: null, key: 5, value: 'B' } 86 | 87 | tree.root.right; 88 | //=> Node { left: null, right: null, key: 13, value: 'C' } 89 | 90 | tree.insert(11, 'D').insert(15, 'E'); 91 | /*=> {9} 92 | * / \ 93 | * {5} {13} 94 | * / \ 95 | * {11} {15} 96 | */ 97 | 98 | tree.size(); 99 | //=> 5 100 | 101 | tree.search(13); 102 | //=> Node { key: 13, value: 'C', 103 | // left: Node { left: null, right: null, key: 11, value: 'D' }, 104 | // right: Node { left: null, right: null, key: 15, value: 'E' } } 105 | 106 | tree.search(25); 107 | //=> null 108 | 109 | tree.includes(11); 110 | //=> true 111 | 112 | tree.includes(100); 113 | //=> false 114 | 115 | tree.height(); 116 | //=> 2 117 | 118 | tree.remove(5); 119 | /*=> {13} 120 | * / \ 121 | * {9} {15} 122 | * \ 123 | * {11} 124 | */ 125 | 126 | tree.root.isRightHeavy(); 127 | //=> false 128 | 129 | tree.root.isLeftHeavy(); 130 | //=> true 131 | 132 | tree.max(); 133 | //=> Node { left: null, right: null, key: 15, value: 'E' } 134 | 135 | tree.maxKey(); 136 | //=> 15 137 | 138 | tree.maxValue(); 139 | //=> 'E' 140 | 141 | tree.min(); 142 | //=> Node { left: null, right: null, key: 9, value: 'A' } 143 | 144 | tree.minKey(); 145 | //=> 9 146 | 147 | tree.minValue(); 148 | //=> 'A' 149 | 150 | tree.remove(15); 151 | /*=> {11} 152 | * / \ 153 | * {9} {13} 154 | */ 155 | 156 | tree.root.isBalanced(); 157 | //=> true 158 | 159 | tree.keys(); 160 | //=> [9, 11, 13] 161 | 162 | tree.values(); 163 | //=> ['A', 'D', 'C'] 164 | ``` 165 | 166 | ## API 167 | 168 | #### tree.`root` 169 | 170 | - Return Type: `Node | null` 171 | 172 | Returns the root node of the tree. 173 | If the tree is empty `null` is returned. 174 | 175 | ```js 176 | const {Tree} = require('avlbinstree'); 177 | 178 | const tree = new Tree(); 179 | 180 | tree.insert(10, 'A'); 181 | // => Tree { root: Node { key: 10, value: 'A', left: null, right: null } } 182 | tree.root; 183 | // => Node { key: 10, value: 'A', left: null, right: null } 184 | ``` 185 | 186 | #### tree.`clear()` 187 | 188 | - Return Type: `Tree` 189 | 190 | Mutates the tree by removing all residing nodes and returns it empty. 191 | 192 | ```js 193 | const {Tree} = require('avlbinstree'); 194 | 195 | const tree = new Tree(); 196 | 197 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 198 | //=> Tree { root: Node { left: [Node], right: [Node], key: 3, value: 'A' } } 199 | tree.size(); 200 | //=> 3 201 | tree.clear(); 202 | //=> Tree { root: null } } 203 | tree.size(); 204 | //=> 0 205 | ``` 206 | 207 | #### tree.`fullNodes()` 208 | 209 | - Return Type: `Array` 210 | 211 | Applies in-order traversal to the tree and stores each traversed full node (node with two non-null children) in an array. 212 | The array is returned at the end of the traversal. 213 | 214 | ```js 215 | const {Tree} = require('avlbinstree'); 216 | 217 | const tree = new Tree(); 218 | 219 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 220 | tree.fullNodes(); 221 | //=> [ 222 | // Node { left: [Node], right: [Node], key: 10, value: 'A' } 223 | // ] 224 | ``` 225 | 226 | #### tree.`height()` 227 | 228 | - Return Type: `Number` 229 | 230 | Returns the maximum distance of any leaf node from the root. 231 | If the tree is empty `-1` is returned. 232 | 233 | ```js 234 | const {Tree} = require('avlbinstree'); 235 | 236 | const tree = new Tree(); 237 | 238 | tree.insert(10, 'A'); 239 | tree.height(); 240 | // => 0 241 | tree.insert(5, 'B').insert(15, 'C').insert(25, 'D'); 242 | tree.height(); 243 | //=> 3 244 | ``` 245 | 246 | #### tree.`includes(key)` 247 | 248 | - Return Type: `Boolean` 249 | 250 | Determines whether the tree includes a node with a certain `key`, returning `true` or `false` as appropriate. 251 | 252 | ##### **`key`** 253 | 254 | - Type: `Number` 255 | 256 | Node `key` to search for. 257 | 258 | ```js 259 | const {Tree} = require('avlbinstree'); 260 | 261 | const tree = new Tree(); 262 | 263 | tree.insert(10, 'A').insert(5, 'B'); 264 | tree.includes(10); 265 | // => true 266 | tree.includes(25); 267 | // => false 268 | tree.includes(5); 269 | // => true 270 | ``` 271 | 272 | #### tree.`inOrder(fn)` 273 | 274 | - Return Type: `Tree` 275 | 276 | Applies in-order traversal (depth-first traversal - LNR) to the tree and executes the provided `fn` function on each traversed node without mutating the tree itself. 277 | 278 | ##### **`fn`** 279 | 280 | - Type: `Function` 281 | 282 | Function to execute on each node. 283 | 284 | ```js 285 | const {Tree} = require('avlbinstree'); 286 | 287 | const tree = new Tree(); 288 | 289 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 290 | tree.inOrder(node => console.log(node.key)); 291 | // => 5 292 | // 10 293 | // 15 294 | ``` 295 | 296 | #### tree.`insert(key, value)` 297 | 298 | - Return Type: `Tree` 299 | 300 | Mutates the tree by inserting a new node at the appropriate location. 301 | 302 | ##### **`key`** 303 | 304 | - Type: `Number` 305 | 306 | Can be any number that will correspond to the `key` of the created node. 307 | Each node has its own unique `key`. 308 | 309 | ##### **`value`** 310 | 311 | - Type: `Any` 312 | 313 | Can be any value that will stored in the created node. 314 | 315 | ```js 316 | const {Tree} = require('avlbinstree'); 317 | 318 | const tree = new Tree(); 319 | 320 | tree.insert(10, 'A'); 321 | // => Tree { root: Node { key: 10, value: 'A', left: null, right: null } } 322 | ``` 323 | 324 | #### tree.`internalNodes()` 325 | 326 | - Return Type: `Array` 327 | 328 | Applies in-order traversal to the tree and stores each traversed internal node (node with at least a single non-null child) in an array. 329 | The array is returned at the end of the traversal. 330 | 331 | ```js 332 | const {Tree} = require('avlbinstree'); 333 | 334 | const tree = new Tree(); 335 | 336 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C').insert(20, 'D'); 337 | tree.internalNodes(); 338 | //=> [ 339 | // Node { left: [Node], right: [Node], key: 10, value: 'A' }, 340 | // Node { left: null, right: [Node], key: 15, value: 'C' } 341 | // ] 342 | ``` 343 | 344 | #### tree.`isComplete()` 345 | 346 | - Return Type: `Boolean` 347 | 348 | The method returns `true` if the tree is a complete binary search tree, which implies that every level, except possibly the last, is completely filled, and all nodes are as far left as possible. 349 | In any other case, the method returns `false`. 350 | 351 | ```js 352 | const {Tree} = require('avlbinstree'); 353 | 354 | const tree = new Tree(); 355 | 356 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 357 | tree.isComplete(); 358 | //=> true 359 | tree.insert(3, 'D'); 360 | tree.isComplete(); 361 | //=> true 362 | tree.insert(20, 'E'); 363 | tree.isComplete(); 364 | //=> false 365 | ``` 366 | 367 | #### tree.`isEmpty()` 368 | 369 | - Return Type: `Boolean` 370 | 371 | Determines whether the tree is empty, returning `true` or `false` as appropriate. 372 | 373 | ```js 374 | const {Tree} = require('avlbinstree'); 375 | 376 | const tree = new Tree(); 377 | 378 | tree.insert(10, 'A'); 379 | tree.isEmpty(); 380 | // => false 381 | ``` 382 | 383 | #### tree.`isFull()` 384 | 385 | - Return Type: `Boolean` 386 | 387 | The method returns `true` if all the nodes residing in the tree are either leaf nodes or full nodes. 388 | In any other case (node degree equal to 1) the method returns `false`. 389 | 390 | ```js 391 | const {Tree} = require('avlbinstree'); 392 | 393 | const tree = new Tree(); 394 | 395 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 396 | tree.isFull(); 397 | //=> true 398 | tree.insert(8, 'D'); 399 | tree.isFull(); 400 | //=> false 401 | ``` 402 | 403 | #### tree.`isPerfect()` 404 | 405 | - Return Type: `Boolean` 406 | 407 | The method returns `true` if all the internal nodes residing in the tree are full nodes (node degree equal to 2) and all leaf nodes are at the same height level. In any other case (node degree equal to 1 or leaf and full nodes are found on the same height level) the method returns `false`. 408 | 409 | ```js 410 | const {Tree} = require('avlbinstree'); 411 | 412 | const tree = new Tree(); 413 | 414 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 415 | tree.isPerfect(); 416 | //=> true 417 | tree.insert(3, 'D').insert(7, 'E').insert(12, 'F').insert(20, 'G'); 418 | tree.isPerfect(); 419 | //=> true 420 | tree.insert(1, 'H'); 421 | tree.isPerfect(); 422 | //=> false 423 | ``` 424 | 425 | #### tree.`keys()` 426 | 427 | - Return Type: `Array` 428 | 429 | Applies in-order traversal to the tree and stores the `key` of each traversed node in an array. 430 | The array is returned at the end of the traversal. 431 | 432 | ```js 433 | const {Tree} = require('avlbinstree'); 434 | 435 | const tree = new Tree(); 436 | 437 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 438 | tree.keys(); 439 | //=> [ 5, 10, 15 ] 440 | ``` 441 | 442 | #### tree.`leafNodes()` 443 | 444 | - Return Type: `Array` 445 | 446 | Applies in-order traversal to the tree and stores each traversed leaf node (node without children) in an array. 447 | The array is returned at the end of the traversal. 448 | 449 | ```js 450 | const {Tree} = require('avlbinstree'); 451 | 452 | const tree = new Tree(); 453 | 454 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 455 | tree.leafNodes(); 456 | //=> [ 457 | // Node { left: null, right: null, key: 5, value: 'B' }, 458 | // Node { left: null, right: null, key: 15, value: 'C' } 459 | // ] 460 | ``` 461 | 462 | #### tree.`levelOrder(fn)` 463 | 464 | - Return Type: `Tree` 465 | 466 | Applies level-order traversal (breadth-first traversal) to the tree and executes the provided `fn` function on each traversed node without mutating the tree itself. 467 | 468 | ##### **`fn`** 469 | 470 | - Type: `Function` 471 | 472 | Function to execute on each node. 473 | 474 | ```js 475 | const {Tree} = require('avlbinstree'); 476 | 477 | const tree = new Tree(); 478 | 479 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 480 | tree.levelOrder(node => console.log(node.key)); 481 | // => 10 482 | // 5 483 | // 15 484 | ``` 485 | 486 | #### tree.`max()` 487 | 488 | - Return Type: `Node | null` 489 | 490 | Returns the right-most node in the tree, thus the node corresponding to the maximum `key`. 491 | 492 | ```js 493 | const {Tree} = require('avlbinstree'); 494 | 495 | const tree = new Tree(); 496 | 497 | tree.insert(10, 'A').insert(15, 'B').insert(25, 'C'); 498 | tree.max(); 499 | // => Node { key: 25, value: 'C', left: null, right: null } 500 | ``` 501 | 502 | #### tree.`maxKey()` 503 | 504 | - Return Type: `Number | null` 505 | 506 | Returns the `key` of right-most node in the tree, thus the maximum `key` in the tree. 507 | 508 | ```js 509 | const {Tree} = require('avlbinstree'); 510 | 511 | const tree = new Tree(); 512 | 513 | tree.insert(10, 'A').insert(15, 'B').insert(25, 'C'); 514 | tree.maxKey(); 515 | // => 25 516 | ``` 517 | 518 | #### tree.`maxValue()` 519 | 520 | - Return Type: `Any | null` 521 | 522 | Returns the `value` of right-most node in the tree, thus the `value` of the node corresponding to the maximum `key`. 523 | 524 | ```js 525 | const {Tree} = require('avlbinstree'); 526 | 527 | const tree = new Tree(); 528 | 529 | tree.insert(10, 'A').insert(15, 'B').insert(25, 'C'); 530 | tree.maxValue(); 531 | // => 'C' 532 | ``` 533 | 534 | #### tree.`min()` 535 | 536 | - Return Type: `Node | null` 537 | 538 | Returns the left-most node in the tree, thus the node corresponding to the minimum `key`. 539 | 540 | ```js 541 | const {Tree} = require('avlbinstree'); 542 | 543 | const tree = new Tree(); 544 | 545 | tree.insert(10, 'A').insert(5, 'B').insert(0, 'C'); 546 | tree.min(); 547 | // => Node { key: 0, value: 'C', left: null, right: null } 548 | ``` 549 | 550 | #### tree.`minKey()` 551 | 552 | - Return Type: `Number | null` 553 | 554 | Returns the `key` of the left-most node in the tree, thus the minimum `key` in the tree. 555 | 556 | ```js 557 | const {Tree} = require('avlbinstree'); 558 | 559 | const tree = new Tree(); 560 | 561 | tree.insert(10, 'A').insert(15, 'B').insert(25, 'C'); 562 | tree.minKey(); 563 | // => 10 564 | ``` 565 | 566 | #### tree.`minValue()` 567 | 568 | - Return Type: `Any | null` 569 | 570 | Returns the `value` of the left-most node in the tree, thus the `value` of the node corresponding to the minimum `key`. 571 | 572 | ```js 573 | const {Tree} = require('avlbinstree'); 574 | 575 | const tree = new Tree(); 576 | 577 | tree.insert(10, 'A').insert(15, 'B').insert(25, 'C'); 578 | tree.maxValue(); 579 | // => 'A' 580 | ``` 581 | 582 | #### tree.`outOrder(fn)` 583 | 584 | - Return Type: `Tree` 585 | 586 | Applies out-order traversal (depth-first traversal - RNL) to the tree and executes the provided `fn` function on each traversed node without mutating the tree itself. 587 | 588 | ##### **`fn`** 589 | 590 | - Type: `Function` 591 | 592 | Function to execute on each node. 593 | 594 | ```js 595 | const {Tree} = require('avlbinstree'); 596 | 597 | const tree = new Tree(); 598 | 599 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 600 | tree.outOrder(node => console.log(node.key)); 601 | // => 15 602 | // 10 603 | // 5 604 | ``` 605 | 606 | #### tree.`postOrder(fn)` 607 | 608 | - Return Type: `Tree` 609 | 610 | Applies post-order traversal (depth-first traversal - LRN) to the tree and executes the provided `fn` function on each traversed node without mutating the tree itself. 611 | 612 | ##### **`fn`** 613 | 614 | - Type: `Function` 615 | 616 | Function to execute on each node. 617 | 618 | ```js 619 | const {Tree} = require('avlbinstree'); 620 | 621 | const tree = new Tree(); 622 | 623 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 624 | tree.postOrder(node => console.log(node.key)); 625 | // => 5 626 | // 15 627 | // 10 628 | ``` 629 | 630 | #### tree.`preOrder(fn)` 631 | 632 | - Return Type: `Tree` 633 | 634 | Applies pre-order traversal (depth-first traversal - NLR) to the tree and executes the provided `fn` function on each traversed node without mutating the tree itself. 635 | 636 | ##### **`fn`** 637 | 638 | - Type: `Function` 639 | 640 | Function to execute on each node. 641 | 642 | ```js 643 | const {Tree} = require('avlbinstree'); 644 | 645 | const tree = new Tree(); 646 | 647 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 648 | tree.preOrder(node => console.log(node.key)); 649 | // => 10 650 | // 5 651 | // 15 652 | ``` 653 | 654 | #### tree.`remove(key)` 655 | 656 | - Return Type: `Tree` 657 | 658 | Mutates the tree by removing the node corresponding to the `key` argument. 659 | 660 | ##### **`key`** 661 | 662 | - Type: `Number` 663 | 664 | Can be any number that corresponds to the `key` of an existing node. 665 | 666 | ```js 667 | const {Tree} = require('avlbinstree'); 668 | 669 | const tree = new Tree(); 670 | 671 | tree.insert(10, 'A'); 672 | tree.remove(10); 673 | //=> Tree { root: null } 674 | ``` 675 | 676 | #### tree.`search(key)` 677 | 678 | - Return Type: `Node | null` 679 | 680 | Determines whether the tree includes a node with a certain `key`, returning the targeted node or `null` as appropriate. 681 | 682 | ##### **`key`** 683 | 684 | - Type: `Number` 685 | 686 | Node `key` to search for. 687 | 688 | ```js 689 | const {Tree} = require('avlbinstree'); 690 | 691 | const tree = new Tree(); 692 | 693 | tree.insert(10, 'A').insert(5, 'B'); 694 | tree.search(10); 695 | // => Node { key: 10, value: 'A', left: [Node], right: null } 696 | tree.search(25); 697 | // => null 698 | tree.search(5); 699 | // => Node { key: 5, value: 'B', left: null, right: null } 700 | ``` 701 | 702 | #### tree.`size()` 703 | 704 | - Return Type: `Number` 705 | 706 | Returns the total number of nodes residing in the tree. 707 | 708 | ```js 709 | const {Tree} = require('avlbinstree'); 710 | 711 | const tree = new Tree(); 712 | 713 | tree.insert(10, 'A').insert(15, 'B').insert(25, 'C'); 714 | tree.size(); 715 | // => 3 716 | ``` 717 | 718 | #### tree.`toArray()` 719 | 720 | - Return Type: `Array` 721 | 722 | Applies in-order traversal to the tree and stores each traversed node in an array. 723 | The array is returned at the end of the traversal. 724 | 725 | ```js 726 | const {Tree} = require('avlbinstree'); 727 | 728 | const tree = new Tree(); 729 | 730 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C').insert(3, 'D').insert(20, 'F'); 731 | tree.toArray(); 732 | //=> [ 733 | // Node { left: null, right: null, key: 3, value: 'D' }, 734 | // Node { left: [Node], right: null, key: 5, value: 'B' }, 735 | // Node { left: [Node], right: [Node], key: 10, value: 'A' }, 736 | // Node { left: null, right: [Node], key: 15, value: 'C' }, 737 | // Node { left: null, right: null, key: 20, value: 'F' } 738 | // ] 739 | ``` 740 | 741 | #### tree.`toPairs()` 742 | 743 | - Return Type: `Array<[Number, Any]>` 744 | 745 | Applies in-order traversal to the tree and for each traversed node stores in an array of size `n`, where `n` the size of the tree, an ordered-pair/2-tuple, where the first element is a `number` corresponding to the `key` of the traversed node, and the last one is a value of type `any`, corresponding to the `value` stored in the traversed node. 746 | The array is returned at the end of the traversal. 747 | 748 | ```js 749 | const {Tree} = require('avlbinstree'); 750 | 751 | const tree = new Tree(); 752 | 753 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C').insert(3, 'D').insert(20, 'F'); 754 | tree.toPairs(); 755 | //=> [ [3, 'D'], [5, 'B'], [10, 'A'], [15, 'C'], [20, 'F'] ] 756 | ``` 757 | 758 | #### tree.`values()` 759 | 760 | - Return Type: `Array` 761 | 762 | Applies in-order traversal to the tree and stores the `value` of each traversed node in an array. 763 | The array is returned at the end of the traversal. 764 | 765 | ```js 766 | const {Tree} = require('avlbinstree'); 767 | 768 | const tree = new Tree(); 769 | 770 | tree.insert(10, 'A').insert(5, 'B').insert(15, 'C'); 771 | tree.keys(); 772 | //=> [ 'B', 'A', 'C' ] 773 | ``` 774 | 775 | Also available, along with the `Tree` exposed class, is the `Node` class, mainly useful for testing purposes, since it can be utilized to compare tree nodes. The class has a binary constructor method, with a `key` and a `value` parameter, corresponding to the key and the value stored in the created instance, respectively. 776 | 777 | #### node.`key` 778 | 779 | - Return Type: `Number` 780 | 781 | The `key` corresponding to the node instance. 782 | 783 | ```js 784 | const {Node} = require('avlbinstree'); 785 | 786 | const node = new Node(10, 'A'); 787 | // => { key:10, value: 'A', left: null, right: null } 788 | node.key; 789 | //=> 10 790 | ``` 791 | 792 | #### node.`value` 793 | 794 | - Return Type: `Any` 795 | 796 | The value that the node contains. 797 | 798 | ```js 799 | const {Node} = require('avlbinstree'); 800 | 801 | const node = new Node(10, 'A'); 802 | // => { key: 10, value: 'A', left: null, right: null } 803 | node.value; 804 | //=> 'A' 805 | node.value = 'B' 806 | // => { key: 10, value: 'B', left: null, right: null } 807 | ``` 808 | 809 | #### node.`left` 810 | 811 | - Return Type: `Node | null` 812 | 813 | The left sub-tree that the node points to. 814 | 815 | ```js 816 | const {Tree} = require('avlbinstree'); 817 | 818 | const tree = new Tree(); 819 | 820 | tree.insert(10, 'A').root; 821 | // => { key: 10, value: 'A', left: null, right: null } 822 | tree.root.left; 823 | //=> null 824 | tree.insert(5, 'B').root; 825 | // => { key: 10, value: 'A', left: { key: 5, value: 'B', left: null, right: null } , right: null } 826 | tree.root.left; 827 | //=> { key: 5, value: 'B', left: null, right: null } 828 | ``` 829 | 830 | #### node.`right` 831 | 832 | - Return Type: `Node | null` 833 | 834 | The right sub-tree that the node points to. 835 | 836 | ```js 837 | const {Tree} = require('avlbinstree'); 838 | 839 | const tree = new Tree(); 840 | 841 | tree.insert(10, 'A').root; 842 | // => { key: 10, value: 'A', left: null, right: null } 843 | tree.root.right; 844 | //=> null 845 | tree.insert(15, 'B').root; 846 | // => { key: 10, value: 'A', left: null , right: { key: 15, value: 'B', left: null, right: null } } 847 | tree.root.right; 848 | //=> { key: 15, value: 'B', left: null, right: null } 849 | ``` 850 | 851 | #### node.`balanceFactor` 852 | 853 | - Return Type: `Number` 854 | 855 | Returns a number corresponding to the balance factor of a node, which is defined as the height difference of its two child sub-trees. 856 | 857 | ```js 858 | const {Tree} = require('avlbinstree'); 859 | 860 | const tree = new Tree(); 861 | 862 | tree.insert(10, 'A').root.balanceFactor; 863 | //=> 0 864 | tree.insert(5, 'B').root.balanceFactor; 865 | //=> 1 866 | tree.remove(5).insert(15, 'C').root.balanceFactor; 867 | //=> -1 868 | ``` 869 | 870 | #### node.`children` 871 | 872 | - Return Type: `Array` 873 | 874 | Returns an array contacting the children of the instance, where the left child, if present, is the first element of the array, and the right child, if present, is the last element of the array. 875 | 876 | ```js 877 | const {Tree} = require('avlbinstree'); 878 | 879 | const tree = new Tree(); 880 | 881 | tree.insert(10, 'A').root.children; 882 | //=> [] 883 | tree.insert(5, 'B').insert(15, 'C').root.children; 884 | // => [ 885 | // { key: 5, value: 'B', left: null , right: null }, 886 | // { key: 15, value: 'C', left: null, right: null } 887 | // ] 888 | ``` 889 | 890 | #### node.`degree` 891 | 892 | - Return Type: `Number` 893 | 894 | Returns the number of sub-trees that the node points to. 895 | 896 | ```js 897 | const {Tree} = require('avlbinstree'); 898 | 899 | const tree = new Tree(); 900 | 901 | tree.insert(10, 'A').root.degree; 902 | //=> 0 903 | tree.insert(5, 'B').root.degree; 904 | //=> 1 905 | tree.insert(15, 'C').root.degree; 906 | //=> 2 907 | ``` 908 | 909 | #### node.`height` 910 | 911 | - Return Type: `Number` 912 | 913 | Returns the maximum distance of any leaf node from the node instance. 914 | 915 | ```js 916 | const {Tree} = require('avlbinstree'); 917 | 918 | const tree = new Tree(); 919 | 920 | tree.insert(10, 'A').insert(5, 'B').insert(10, 'C').insert(25, 'D'); 921 | tree.root.height; 922 | //=> 2 923 | tree.root.right.height(); 924 | //=> 1 925 | ``` 926 | 927 | #### node.`isBalanced()` 928 | 929 | - Return Type: `Boolean` 930 | 931 | Determines whether a node is a balanced (has a balance factor equal to 0), returning `true` or `false` as appropriate. 932 | 933 | ```js 934 | const {Tree} = require('avlbinstree'); 935 | 936 | const tree = new Tree(); 937 | 938 | tree.insert(10, 'A').root.isBalanced(); 939 | //=> true 940 | tree.insert(5, 'B').root.isBalanced(); 941 | //=> false 942 | ``` 943 | 944 | #### node.`isFull()` 945 | 946 | - Return Type: `Boolean` 947 | 948 | Determines whether a node is a full node (has two non-null children), returning `true` or `false` as appropriate. 949 | 950 | ```js 951 | const {Tree} = require('avlbinstree'); 952 | 953 | const tree = new Tree(); 954 | 955 | tree.insert(10, 'A').root.isFull(); 956 | //=> false 957 | tree.insert(5, 'B').insert(15, 'C').root.isFull(); 958 | //=> true 959 | ``` 960 | 961 | #### node.`isInternal()` 962 | 963 | - Return Type: `Boolean` 964 | 965 | Determines whether a node is an internal node (has at least one non-null child), returning `true` or `false` as appropriate. 966 | 967 | ```js 968 | const {Tree} = require('avlbinstree'); 969 | 970 | const tree = new Tree(); 971 | 972 | tree.insert(10, 'A').root.isInternal(); 973 | //=> false 974 | tree.insert(5, 'B').root.isInternal(); 975 | //=> true 976 | ``` 977 | 978 | #### node.`isLeaf()` 979 | 980 | - Return Type: `Boolean` 981 | 982 | Determines whether a node is a leaf node (has no children), returning `true` or `false` as appropriate. 983 | 984 | ```js 985 | const {Tree} = require('avlbinstree'); 986 | 987 | const tree = new Tree(); 988 | 989 | tree.insert(10, 'A').root.isLeaf(); 990 | //=> true 991 | tree.insert(5, 'B').root.isLeaf(); 992 | //=> false 993 | ``` 994 | 995 | #### node.`isLeftHeavy()` 996 | 997 | - Return Type: `Boolean` 998 | 999 | Determines whether a node is left heavy (has a balance factor greater than zero), returning `true` or `false` as appropriate. 1000 | 1001 | ```js 1002 | const {Tree} = require('avlbinstree'); 1003 | 1004 | const tree = new Tree(); 1005 | 1006 | tree.insert(10, 'A').root.isLeftHeavy(); 1007 | //=> false 1008 | tree.insert(5, 'B').root.isLeftPartial(); 1009 | //=> true 1010 | tree.remove(5).insert(10, 'C').root.isLeftPartial(); 1011 | //=> false 1012 | ``` 1013 | 1014 | #### node.`isLeftPartial()` 1015 | 1016 | - Return Type: `Boolean` 1017 | 1018 | Determines whether a node is a left partial node (has ony one left non-null child), returning `true` or `false` as appropriate. 1019 | 1020 | ```js 1021 | const {Tree} = require('avlbinstree'); 1022 | 1023 | const tree = new Tree(); 1024 | 1025 | tree.insert(10, 'A').root.isLeftPartial(); 1026 | //=> false 1027 | tree.insert(5, 'B').root.isLeftPartial(); 1028 | //=> true 1029 | ``` 1030 | 1031 | #### node.`isPartial()` 1032 | 1033 | - Return Type: `Boolean` 1034 | 1035 | Determines whether a node is a partial node (has ony one non-null child), returning `true` or `false` as appropriate. 1036 | 1037 | ```js 1038 | const {Tree} = require('avlbinstree'); 1039 | 1040 | const tree = new Tree(); 1041 | 1042 | tree.insert(10, 'A').root.isPartial(); 1043 | //=> false 1044 | tree.insert(15, 'B').root.isPartial(); 1045 | //=> true 1046 | ``` 1047 | 1048 | #### node.`isRightHeavy()` 1049 | 1050 | - Return Type: `Boolean` 1051 | 1052 | Determines whether a node is right heavy (has a balance factor less than zero), returning `true` or `false` as appropriate. 1053 | 1054 | ```js 1055 | const {Tree} = require('avlbinstree'); 1056 | 1057 | const tree = new Tree(); 1058 | 1059 | tree.insert(10, 'A').root.isRightHeavy(); 1060 | //=> false 1061 | tree.insert(15, 'C').root.isRightHeavy(); 1062 | //=> true 1063 | tree.remove(15).insert(5, 'B').root.isRightHeavy(); 1064 | //=> false 1065 | ``` 1066 | 1067 | #### node.`isRightPartial()` 1068 | 1069 | - Return Type: `Boolean` 1070 | 1071 | Determines whether a node is a right partial node (has ony one right non-null child), returning `true` or `false` as appropriate. 1072 | 1073 | ```js 1074 | const {Tree} = require('avlbinstree'); 1075 | 1076 | const tree = new Tree(); 1077 | 1078 | tree.insert(10, 'A').root.isRightPartial(); 1079 | //=> false 1080 | tree.insert(15, 'B').root.isRightPartial(); 1081 | //=> true 1082 | ``` 1083 | 1084 | #### node.`leftChildHeight()` 1085 | 1086 | - Return Type: `Number` 1087 | 1088 | Returns the maximum distance of any leaf node from the left child of the parent node instance. If the parent node has no left child, then `-1` is returned. 1089 | 1090 | ```js 1091 | const {Tree} = require('avlbinstree'); 1092 | 1093 | const tree = new Tree(); 1094 | 1095 | tree.insert(10, 'A').root.leftChildHeight(); 1096 | //=> -1 1097 | tree.insert(5, 'B').root.leftChildHeight(); 1098 | //=> 0 1099 | ``` 1100 | 1101 | #### node.`maxChildHeight()` 1102 | 1103 | - Return Type: `Number` 1104 | 1105 | Returns the maximum between the heights of the two child nodes of parent instance. If the parent node has no children, then `-1` is returned. 1106 | 1107 | ```js 1108 | const {Tree} = require('avlbinstree'); 1109 | 1110 | const tree = new Tree(); 1111 | 1112 | tree.insert(10, 'A').root.maxChildHeight(); 1113 | //=> -1 1114 | tree.insert(15, 'B').root.maxChildHeight(); 1115 | //=> 0 1116 | tree.insert(5, 'C').root.maxChildHeight(); 1117 | //=> 0 1118 | tree.insert(1, 'D').root.maxChildHeight(); 1119 | //=> 1 1120 | ``` 1121 | 1122 | #### node.`rightChildHeight()` 1123 | 1124 | - Return Type: `Number` 1125 | 1126 | Returns the maximum distance of any leaf node from the right child of the parent node instance. If the parent node has no right child, then `-1` is returned. 1127 | 1128 | ```js 1129 | const {Tree} = require('avlbinstree'); 1130 | 1131 | const tree = new Tree(); 1132 | 1133 | tree.insert(10, 'A').root.rightChildHeight(); 1134 | //=> -1 1135 | tree.insert(15, 'B').root.rightChildHeight(); 1136 | //=> 0 1137 | ``` 1138 | 1139 | #### node.`toPair()` 1140 | 1141 | - Return Type: `[Number, Any]` 1142 | 1143 | Returns an ordered-pair/2-tuple, where the first element is a number corresponding to the `key` of the node, and the last one is a value, that can be of any type, corresponding to the `value` stored in the node. 1144 | 1145 | ```js 1146 | const {Node, Tree} = require('avlbinstree'); 1147 | 1148 | const tree = new Tree(); 1149 | const node = new Node(5, 'B'); 1150 | 1151 | node.toPair(); 1152 | //=> [5, 'B'] 1153 | tree.insert(10, 'A').root.toPair(); 1154 | //=> [10, 'A'] 1155 | ``` 1156 | 1157 | ## Development 1158 | 1159 | For more info on how to contribute to the project, please read the [contributing guidelines](https://github.com/klaussinani/avlbinstree/blob/master/contributing.md). 1160 | 1161 | - Fork the repository and clone it to your machine 1162 | - Navigate to your local fork: `cd avlbinstree` 1163 | - Install the project dependencies: `npm install` or `yarn install` 1164 | - Lint the code and run the tests: `npm test` or `yarn test` 1165 | 1166 | ## Related 1167 | 1168 | - [binoheap](https://github.com/klaussinani/binoheap) - Binomial heaps for ES6 1169 | - [binstree](https://github.com/klaussinani/binstree) - Binary search trees for ES6 1170 | - [doublie](https://github.com/klaussinani/doublie) - Doubly circular & linear linked lists for ES6 1171 | - [dsforest](https://github.com/klaussinani/dsforest) - Disjoint-set forests for ES6 1172 | - [kiu](https://github.com/klaussinani/kiu) - FIFO Queues for ES6 1173 | - [mheap](https://github.com/klaussinani/mheap) - Binary min & max heaps for ES6 1174 | - [prioqueue](https://github.com/klaussinani/prioqueue) - Priority queues for ES6 1175 | - [shtack](https://github.com/klaussinani/shtack) - LIFO Stacks for ES6 1176 | - [singlie](https://github.com/klaussinani/singlie) - Singly circular & linear linked lists for ES6 1177 | 1178 | ## Team 1179 | 1180 | - Klaus Sinani [(@klaussinani)](https://github.com/klaussinani) 1181 | 1182 | ## License 1183 | 1184 | [MIT](https://github.com/klaussinani/avlbinstree/blob/master/license.md) 1185 | -------------------------------------------------------------------------------- /src/node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class Node { 4 | constructor(key, value) { 5 | this._height = 0; 6 | this._left = null; 7 | this._right = null; 8 | this._key = key; 9 | this._value = value; 10 | } 11 | 12 | _rotateLeft() { 13 | const {right} = this; 14 | this.right = right.left; 15 | right.left = this; 16 | 17 | this._height = this.maxChildHeight() + 1; 18 | right._height = right.maxChildHeight() + 1; 19 | 20 | return right; 21 | } 22 | 23 | _rotateRight() { 24 | const {left} = this; 25 | this.left = left.right; 26 | left.right = this; 27 | 28 | this._height = this.maxChildHeight() + 1; 29 | left._height = left.maxChildHeight() + 1; 30 | 31 | return left; 32 | } 33 | 34 | get balanceFactor() { 35 | return this.leftChildHeight() - this.rightChildHeight(); 36 | } 37 | 38 | get children() { 39 | const children = []; 40 | 41 | if (this.left) { 42 | children.push(this.left); 43 | } 44 | 45 | if (this.right) { 46 | children.push(this.right); 47 | } 48 | 49 | return children; 50 | } 51 | 52 | get degree() { 53 | return this.children.length; 54 | } 55 | 56 | get height() { 57 | return this._height; 58 | } 59 | 60 | get left() { 61 | return this._left; 62 | } 63 | 64 | set left(node) { 65 | if (node && node.key >= this.key) { 66 | throw new Error('Left child node key must be less than the parent node key'); 67 | } 68 | 69 | this._left = node; 70 | } 71 | 72 | get right() { 73 | return this._right; 74 | } 75 | 76 | set right(node) { 77 | if (node && node.key <= this.key) { 78 | throw new Error('Right child node key must be greater than the parent node key'); 79 | } 80 | 81 | this._right = node; 82 | } 83 | 84 | get key() { 85 | return this._key; 86 | } 87 | 88 | get value() { 89 | return this._value; 90 | } 91 | 92 | set value(value) { 93 | this._value = value; 94 | } 95 | 96 | isBalanced() { 97 | return this.balanceFactor === 0; 98 | } 99 | 100 | isFull() { 101 | return this.left !== null && this.right !== null; 102 | } 103 | 104 | isInternal() { 105 | return this.left !== null || this.right !== null; 106 | } 107 | 108 | isLeaf() { 109 | return !this.left && !this.right; 110 | } 111 | 112 | isLeftHeavy() { 113 | return this.balanceFactor > 0; 114 | } 115 | 116 | isLeftPartial() { 117 | return this.left !== null && !this.right; 118 | } 119 | 120 | isPartial() { 121 | return this.isLeftPartial() || this.isRightPartial(); 122 | } 123 | 124 | isRightHeavy() { 125 | return this.balanceFactor < 0; 126 | } 127 | 128 | isRightPartial() { 129 | return !this.left && this.right !== null; 130 | } 131 | 132 | leftChildHeight() { 133 | if (this.left) { 134 | return this.left.height; 135 | } 136 | 137 | return -1; 138 | } 139 | 140 | maxChildHeight() { 141 | return Math.max(this.leftChildHeight(), this.rightChildHeight()); 142 | } 143 | 144 | rightChildHeight() { 145 | if (this.right) { 146 | return this.right.height; 147 | } 148 | 149 | return -1; 150 | } 151 | 152 | toPair() { 153 | return [this.key, this.value]; 154 | } 155 | } 156 | 157 | module.exports = Node; 158 | -------------------------------------------------------------------------------- /src/tree.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Node = require('./node'); 3 | 4 | class Tree { 5 | constructor() { 6 | this._root = null; 7 | } 8 | 9 | get root() { 10 | return this._root; 11 | } 12 | 13 | _balanceSubtree(node) { 14 | if (node.balanceFactor === 2) { 15 | if (node.left.isRightHeavy()) { 16 | node.left = node.left._rotateLeft(); 17 | } 18 | 19 | return node._rotateRight(); 20 | } 21 | 22 | if (node.balanceFactor === -2) { 23 | if (node.right.isLeftHeavy()) { 24 | node.right = node.right._rotateRight(); 25 | } 26 | 27 | return node._rotateLeft(); 28 | } 29 | 30 | return node; 31 | } 32 | 33 | _insert(key, value, target) { 34 | if (!target) { 35 | return new Node(key, value); 36 | } 37 | 38 | if (key === target.key) { 39 | return target; 40 | } 41 | 42 | if (key < target.key) { 43 | target.left = this._insert(key, value, target.left); 44 | } else { 45 | target.right = this._insert(key, value, target.right); 46 | } 47 | 48 | target._height = target.maxChildHeight() + 1; 49 | 50 | return this._balanceSubtree(target); 51 | } 52 | 53 | _isBalanced() { 54 | let {root: current} = this; 55 | 56 | if (current) { 57 | let height = 0; 58 | const queue = [current]; 59 | let [minHeight, maxHeight] = [Infinity, -Infinity]; 60 | 61 | while (queue.length > 0) { 62 | let nodes = queue.length; 63 | 64 | while (nodes > 0) { 65 | current = queue.shift(); 66 | 67 | if (current.isLeaf()) { 68 | minHeight = minHeight > height ? height : minHeight; 69 | maxHeight = maxHeight < height ? height : maxHeight; 70 | } else { 71 | queue.push(...current.children); 72 | } 73 | 74 | nodes--; 75 | } 76 | 77 | if (maxHeight - minHeight > 1) { 78 | return false; 79 | } 80 | 81 | height++; 82 | } 83 | } 84 | 85 | return true; 86 | } 87 | 88 | _min(node) { 89 | let min = node; 90 | 91 | if (min) { 92 | while (min.left) { 93 | min = min.left; 94 | } 95 | } 96 | 97 | return min; 98 | } 99 | 100 | _prop(obj, p) { 101 | if (obj) { 102 | return obj[p]; 103 | } 104 | 105 | return null; 106 | } 107 | 108 | _remove(key, node) { 109 | if (key < node.key) { 110 | node.left = this._remove(key, node.left); 111 | } else if (key > node.key) { 112 | node.right = this._remove(key, node.right); 113 | } else if (node.isLeaf()) { 114 | node = null; 115 | } else if (node.isRightPartial()) { 116 | node = node.right; 117 | } else if (node.isLeftPartial()) { 118 | node = node.left; 119 | } else { 120 | const successor = this._min(node.right); 121 | node._key = successor.key; 122 | node.value = successor.value; 123 | node.right = this._remove(successor.key, node.right); 124 | } 125 | 126 | if (!node) { 127 | return node; 128 | } 129 | 130 | node._height = node.maxChildHeight() + 1; 131 | 132 | return this._balanceSubtree(node); 133 | } 134 | 135 | clear() { 136 | this._root = null; 137 | return this; 138 | } 139 | 140 | fullNodes() { 141 | const nodes = []; 142 | 143 | this.inOrder(node => { 144 | if (node.isFull()) { 145 | nodes.push(node); 146 | } 147 | }); 148 | 149 | return nodes; 150 | } 151 | 152 | height() { 153 | const {root} = this; 154 | 155 | if (root) { 156 | return root.height; 157 | } 158 | 159 | return -1; 160 | } 161 | 162 | includes(key) { 163 | let {root: current} = this; 164 | 165 | while (current) { 166 | if (key === current.key) { 167 | return true; 168 | } 169 | 170 | current = key < current.key ? current.left : current.right; 171 | } 172 | 173 | return false; 174 | } 175 | 176 | inOrder(fn) { 177 | const stack = []; 178 | let {root: current} = this; 179 | 180 | while (current || stack.length > 0) { 181 | if (current) { 182 | stack.push(current); 183 | current = current.left; 184 | } else { 185 | current = stack.pop(); 186 | fn(current); 187 | current = current.right; 188 | } 189 | } 190 | 191 | return this; 192 | } 193 | 194 | insert(key, value) { 195 | this._root = this._insert(key, value, this._root); 196 | return this; 197 | } 198 | 199 | internalNodes() { 200 | const nodes = []; 201 | 202 | this.inOrder(node => { 203 | if (node.isInternal()) { 204 | nodes.push(node); 205 | } 206 | }); 207 | 208 | return nodes; 209 | } 210 | 211 | isComplete() { 212 | let {root: current} = this; 213 | 214 | if (current) { 215 | const queue = [current]; 216 | let sawNonFull = false; 217 | 218 | while (queue.length > 0) { 219 | current = queue.shift(); 220 | 221 | if (current.isRightPartial()) { 222 | return false; 223 | } 224 | 225 | if (current.isLeaf()) { 226 | sawNonFull = true; 227 | } else { 228 | if (sawNonFull) { 229 | return false; 230 | } 231 | 232 | const {children} = current; 233 | sawNonFull = children.length < 2; 234 | queue.push(...children); 235 | } 236 | } 237 | } 238 | 239 | return true; 240 | } 241 | 242 | isEmpty() { 243 | return !this.root; 244 | } 245 | 246 | isFull() { 247 | let {root: current} = this; 248 | 249 | if (current) { 250 | const queue = [current]; 251 | 252 | while (queue.length > 0) { 253 | current = queue.shift(); 254 | 255 | if (current.degree === 1) { 256 | return false; 257 | } 258 | 259 | if (current.isFull()) { 260 | queue.push(...current.children); 261 | } 262 | } 263 | } 264 | 265 | return true; 266 | } 267 | 268 | isPerfect() { 269 | let {root: current} = this; 270 | 271 | if (current) { 272 | let currentDepth = -1; 273 | const queue = [current]; 274 | const {height: leafDepth} = current; 275 | 276 | while (queue.length > 0) { 277 | currentDepth += 1; 278 | let {length: nodes} = queue; 279 | 280 | while (nodes > 0) { 281 | current = queue.shift(); 282 | 283 | if (current.isPartial()) { 284 | return false; 285 | } 286 | 287 | if (current.isLeaf() && leafDepth !== currentDepth) { 288 | return false; 289 | } 290 | 291 | queue.push(...current.children); 292 | nodes--; 293 | } 294 | } 295 | } 296 | 297 | return true; 298 | } 299 | 300 | keys() { 301 | const keys = []; 302 | this.inOrder(node => keys.push(node.key)); 303 | return keys; 304 | } 305 | 306 | leafNodes() { 307 | const nodes = []; 308 | 309 | this.inOrder(node => { 310 | if (node.isLeaf()) { 311 | nodes.push(node); 312 | } 313 | }); 314 | 315 | return nodes; 316 | } 317 | 318 | levelOrder(fn) { 319 | let {root: current} = this; 320 | 321 | if (current) { 322 | const queue = []; 323 | queue.push(current); 324 | 325 | while (queue.length > 0) { 326 | current = queue.shift(); 327 | fn(current); 328 | queue.push(...current.children); 329 | } 330 | } 331 | 332 | return this; 333 | } 334 | 335 | max() { 336 | let {root: max} = this; 337 | 338 | if (max) { 339 | while (max.right) { 340 | max = max.right; 341 | } 342 | } 343 | 344 | return max; 345 | } 346 | 347 | maxKey() { 348 | return this._prop(this.max(), 'key'); 349 | } 350 | 351 | maxValue() { 352 | return this._prop(this.max(), 'value'); 353 | } 354 | 355 | min() { 356 | return this._min(this.root); 357 | } 358 | 359 | minKey() { 360 | return this._prop(this.min(), 'key'); 361 | } 362 | 363 | minValue() { 364 | return this._prop(this.min(), 'value'); 365 | } 366 | 367 | outOrder(fn) { 368 | const stack = []; 369 | let {root: current} = this; 370 | 371 | while (current || stack.length > 0) { 372 | if (current) { 373 | stack.push(current); 374 | current = current.right; 375 | } else { 376 | current = stack.pop(); 377 | fn(current); 378 | current = current.left; 379 | } 380 | } 381 | 382 | return this; 383 | } 384 | 385 | postOrder(fn) { 386 | let last = null; 387 | const stack = []; 388 | let {root: current} = this; 389 | 390 | while (current || stack.length > 0) { 391 | if (current) { 392 | stack.push(current); 393 | current = current.left; 394 | } else { 395 | const recent = stack[stack.length - 1]; 396 | 397 | if (recent.right && recent.right !== last) { 398 | current = recent.right; 399 | } else { 400 | fn(recent); 401 | last = stack.pop(); 402 | } 403 | } 404 | } 405 | 406 | return this; 407 | } 408 | 409 | preOrder(fn) { 410 | let {root: current} = this; 411 | 412 | if (current) { 413 | const stack = [current]; 414 | 415 | while (stack.length > 0) { 416 | current = stack.pop(); 417 | fn(current); 418 | 419 | if (current.right) { 420 | stack.push(current.right); 421 | } 422 | 423 | if (current.left) { 424 | stack.push(current.left); 425 | } 426 | } 427 | } 428 | 429 | return this; 430 | } 431 | 432 | remove(key) { 433 | const {root} = this; 434 | 435 | if (root) { 436 | this._root = this._remove(key, root); 437 | } 438 | 439 | return this; 440 | } 441 | 442 | search(key) { 443 | let {root: current} = this; 444 | 445 | while (current) { 446 | if (key === current.key) { 447 | return current; 448 | } 449 | 450 | current = key < current.key ? current.left : current.right; 451 | } 452 | 453 | return current; 454 | } 455 | 456 | size() { 457 | let size = 0; 458 | this.inOrder(() => size++); 459 | return size; 460 | } 461 | 462 | toArray() { 463 | const array = []; 464 | this.inOrder(node => array.push(node)); 465 | return array; 466 | } 467 | 468 | toPairs() { 469 | const array = []; 470 | this.inOrder(node => array.push(node.toPair())); 471 | return array; 472 | } 473 | 474 | values() { 475 | const values = []; 476 | this.inOrder(node => values.push(node.value)); 477 | return values; 478 | } 479 | } 480 | 481 | module.exports = Tree; 482 | -------------------------------------------------------------------------------- /test/empty.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const test = require('ava'); 3 | const {Tree} = require('../.'); 4 | 5 | const tree = new Tree(); 6 | 7 | test('root', t => { 8 | t.is(tree.root, null); 9 | }); 10 | 11 | test('clear', t => { 12 | t.deepEqual(tree.clear(), new Tree()); 13 | }); 14 | 15 | test('fullNodes', t => { 16 | t.deepEqual(tree.fullNodes(), []); 17 | }); 18 | 19 | test('height', t => { 20 | t.is(tree.height(), -1); 21 | }); 22 | 23 | test('includes', t => { 24 | t.false(tree.includes(10)); 25 | }); 26 | 27 | test('inOrder', t => { 28 | const array = []; 29 | t.deepEqual(tree.inOrder(x => array.push(x)), tree); 30 | t.deepEqual(array, []); 31 | }); 32 | 33 | test('internalNodes', t => { 34 | t.deepEqual(tree.internalNodes(), []); 35 | }); 36 | 37 | test('isComplete', t => { 38 | t.true(tree.isComplete()); 39 | }); 40 | 41 | test('isEmpty', t => { 42 | t.true(tree.isEmpty()); 43 | }); 44 | 45 | test('isFull', t => { 46 | t.true(tree.isFull()); 47 | }); 48 | 49 | test('isPerfect', t => { 50 | t.true(tree.isPerfect()); 51 | }); 52 | 53 | test('keys', t => { 54 | t.deepEqual(tree.keys(), []); 55 | }); 56 | 57 | test('leafNodes', t => { 58 | t.deepEqual(tree.leafNodes(), []); 59 | }); 60 | 61 | test('levelOrder', t => { 62 | const array = []; 63 | t.deepEqual(tree.levelOrder(x => array.push(x)), tree); 64 | t.deepEqual(array, []); 65 | }); 66 | 67 | test('max', t => { 68 | t.is(tree.max(), null); 69 | }); 70 | 71 | test('maxKey', t => { 72 | t.is(tree.maxKey(), null); 73 | }); 74 | 75 | test('maxValue', t => { 76 | t.is(tree.maxValue(), null); 77 | }); 78 | 79 | test('min', t => { 80 | t.is(tree.min(), null); 81 | }); 82 | 83 | test('minKey', t => { 84 | t.is(tree.minKey(), null); 85 | }); 86 | 87 | test('minValue', t => { 88 | t.is(tree.minValue(), null); 89 | }); 90 | 91 | test('outOrder', t => { 92 | const array = []; 93 | t.deepEqual(tree.outOrder(x => array.push(x)), tree); 94 | t.deepEqual(array, []); 95 | }); 96 | 97 | test('postOrder', t => { 98 | const array = []; 99 | t.deepEqual(tree.postOrder(x => array.push(x)), tree); 100 | t.deepEqual(array, []); 101 | }); 102 | 103 | test('preOrder', t => { 104 | const array = []; 105 | t.deepEqual(tree.preOrder(x => array.push(x)), tree); 106 | t.deepEqual(array, []); 107 | }); 108 | 109 | test('remove', t => { 110 | t.deepEqual(tree.remove(10), tree); 111 | }); 112 | 113 | test('search', t => { 114 | t.is(tree.search(10), null); 115 | }); 116 | 117 | test('size', t => { 118 | t.is(tree.size(), 0); 119 | }); 120 | 121 | test('toArray', t => { 122 | t.deepEqual(tree.toArray(), []); 123 | }); 124 | 125 | test('toPairs', t => { 126 | t.deepEqual(tree.toPairs(), []); 127 | }); 128 | 129 | test('values', t => { 130 | t.deepEqual(tree.values(), []); 131 | }); 132 | -------------------------------------------------------------------------------- /test/multiple.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const test = require('ava'); 3 | const {Node, Tree} = require('../.'); 4 | 5 | const tree = new Tree(); 6 | 7 | test('insert', t => { 8 | tree 9 | .insert(10, 'A') 10 | .insert(5, 'B') 11 | .insert(15, 'C'); 12 | 13 | t.true(tree._isBalanced()); 14 | 15 | t.is(tree.root.key, 10); 16 | t.is(tree.root.value, 'A'); 17 | t.is(tree.root.left.key, 5); 18 | t.is(tree.root.left.value, 'B'); 19 | t.is(tree.root.left.left, null); 20 | t.is(tree.root.left.right, null); 21 | t.is(tree.root.right.key, 15); 22 | t.is(tree.root.right.value, 'C'); 23 | t.is(tree.root.right.left, null); 24 | t.is(tree.root.right.right, null); 25 | }); 26 | 27 | test('insert duplicate', t => { 28 | tree.clear() 29 | .insert(10, 'A'); 30 | 31 | t.true(tree._isBalanced()); 32 | 33 | t.deepEqual(tree.insert(10, 'A'), tree); 34 | 35 | t.true(tree._isBalanced()); 36 | }); 37 | 38 | test('insert LL case', t => { 39 | tree.clear() 40 | .insert(5, 'A') 41 | .insert(3, 'B') 42 | .insert(2, 'C'); 43 | 44 | t.true(tree._isBalanced()); 45 | 46 | t.is(tree.root.key, 3); 47 | t.is(tree.root.value, 'B'); 48 | t.is(tree.root.left.key, 2); 49 | t.is(tree.root.left.value, 'C'); 50 | t.is(tree.root.left.left, null); 51 | t.is(tree.root.left.right, null); 52 | t.is(tree.root.right.key, 5); 53 | t.is(tree.root.right.value, 'A'); 54 | t.is(tree.root.right.left, null); 55 | t.is(tree.root.right.right, null); 56 | }); 57 | 58 | test('insert LR case', t => { 59 | tree.clear() 60 | .insert(5, 'A') 61 | .insert(3, 'B') 62 | .insert(4, 'C'); 63 | 64 | t.true(tree._isBalanced()); 65 | 66 | t.is(tree.root.key, 4); 67 | t.is(tree.root.value, 'C'); 68 | t.is(tree.root.left.key, 3); 69 | t.is(tree.root.left.value, 'B'); 70 | t.is(tree.root.left.left, null); 71 | t.is(tree.root.left.right, null); 72 | t.is(tree.root.right.key, 5); 73 | t.is(tree.root.right.value, 'A'); 74 | t.is(tree.root.right.left, null); 75 | t.is(tree.root.right.right, null); 76 | }); 77 | 78 | test('insert RR case', t => { 79 | tree.clear() 80 | .insert(3, 'A') 81 | .insert(5, 'B') 82 | .insert(7, 'C'); 83 | 84 | t.true(tree._isBalanced()); 85 | 86 | t.is(tree.root.key, 5); 87 | t.is(tree.root.value, 'B'); 88 | t.is(tree.root.left.key, 3); 89 | t.is(tree.root.left.value, 'A'); 90 | t.is(tree.root.left.left, null); 91 | t.is(tree.root.left.right, null); 92 | t.is(tree.root.right.key, 7); 93 | t.is(tree.root.right.value, 'C'); 94 | t.is(tree.root.right.left, null); 95 | t.is(tree.root.right.right, null); 96 | }); 97 | 98 | test('insert RL case', t => { 99 | tree.clear() 100 | .insert(3, 'A') 101 | .insert(5, 'B') 102 | .insert(4, 'C'); 103 | 104 | t.true(tree._isBalanced()); 105 | 106 | t.is(tree.root.key, 4); 107 | t.is(tree.root.value, 'C'); 108 | t.is(tree.root.left.key, 3); 109 | t.is(tree.root.left.value, 'A'); 110 | t.is(tree.root.left.left, null); 111 | t.is(tree.root.left.right, null); 112 | t.is(tree.root.right.key, 5); 113 | t.is(tree.root.right.value, 'B'); 114 | t.is(tree.root.right.left, null); 115 | t.is(tree.root.right.right, null); 116 | }); 117 | 118 | test('root', t => { 119 | const node = new Node(10, 'A'); 120 | node.left = new Node(5, 'B'); 121 | node.right = new Node(15, 'C'); 122 | node._height = node.maxChildHeight() + 1; 123 | 124 | tree.clear() 125 | .insert(10, 'A') 126 | .insert(5, 'B') 127 | .insert(15, 'C'); 128 | 129 | t.deepEqual(tree.root, node); 130 | }); 131 | 132 | test('clear', t => { 133 | t.deepEqual(tree.clear(), new Tree()); 134 | t.is(tree.root, null); 135 | }); 136 | 137 | test('fullNodes', t => { 138 | tree.clear() 139 | .insert(10, 'A') 140 | .insert(5, 'B') 141 | .insert(15, 'C') 142 | .insert(3, 'D') 143 | .insert(7, 'E'); 144 | 145 | t.deepEqual(tree.fullNodes().map(x => x.toPair()), [[5, 'B'], [10, 'A']]); 146 | }); 147 | 148 | test('height', t => { 149 | tree.clear() 150 | .insert(10, 'A') 151 | .insert(5, 'B') 152 | .insert(15, 'C') 153 | .insert(3, 'D') 154 | .insert(7, 'E'); 155 | 156 | t.is(tree.height(), 2); 157 | }); 158 | 159 | test('includes', t => { 160 | tree.clear() 161 | .insert(10, 'A') 162 | .insert(5, 'B') 163 | .insert(15, 'C') 164 | .insert(3, 'D') 165 | .insert(7, 'E'); 166 | 167 | t.true(tree.includes(7)); 168 | t.true(tree.includes(3)); 169 | t.false(tree.includes(50)); 170 | }); 171 | 172 | test('inOrder', t => { 173 | tree.clear() 174 | .insert(10, 'A') 175 | .insert(5, 'B') 176 | .insert(15, 'C') 177 | .insert(3, 'D') 178 | .insert(7, 'E'); 179 | 180 | const array = []; 181 | t.deepEqual(tree.inOrder(x => array.push(x.toPair())), tree); 182 | t.deepEqual(array, [[3, 'D'], [5, 'B'], [7, 'E'], [10, 'A'], [15, 'C']]); 183 | }); 184 | 185 | test('internalNodes', t => { 186 | tree.clear() 187 | .insert(10, 'A') 188 | .insert(5, 'B') 189 | .insert(15, 'C') 190 | .insert(3, 'D') 191 | .insert(7, 'E'); 192 | 193 | t.deepEqual(tree.internalNodes().map(x => x.toPair()), [[5, 'B'], [10, 'A']]); 194 | }); 195 | 196 | test('isComplete', t => { 197 | tree.clear() 198 | .insert(10, 'A') 199 | .insert(5, 'B') 200 | .insert(15, 'C') 201 | .insert(3, 'D') 202 | .insert(7, 'E'); 203 | 204 | t.true(tree.isComplete()); 205 | tree.insert(30, 'F'); 206 | t.false(tree.isComplete()); 207 | tree 208 | .remove(30) 209 | .remove(15); 210 | t.false(tree.isComplete()); 211 | }); 212 | 213 | test('isEmpty', t => { 214 | tree.clear() 215 | .insert(10, 'A') 216 | .insert(5, 'B') 217 | .insert(15, 'C') 218 | .insert(3, 'D') 219 | .insert(7, 'E'); 220 | 221 | t.false(tree.isEmpty()); 222 | tree.clear(); 223 | t.true(tree.isEmpty()); 224 | }); 225 | 226 | test('isFull', t => { 227 | tree.clear() 228 | .insert(10, 'A') 229 | .insert(5, 'B') 230 | .insert(15, 'C') 231 | .insert(3, 'D') 232 | .insert(7, 'E'); 233 | 234 | t.true(tree.isFull()); 235 | tree.insert(12, 'F'); 236 | t.false(tree.isFull()); 237 | }); 238 | 239 | test('isPerfect', t => { 240 | tree.clear() 241 | .insert(10, 'A') 242 | .insert(5, 'B') 243 | .insert(15, 'C') 244 | .insert(3, 'D') 245 | .insert(7, 'E'); 246 | 247 | t.false(tree.isPerfect()); 248 | tree 249 | .insert(12, 'G') 250 | .insert(17, 'H'); 251 | t.true(tree.isPerfect()); 252 | tree.remove(7); 253 | t.false(tree.isPerfect()); 254 | }); 255 | 256 | test('keys', t => { 257 | tree.clear() 258 | .insert(10, 'A') 259 | .insert(5, 'B') 260 | .insert(15, 'C') 261 | .insert(3, 'D') 262 | .insert(7, 'E') 263 | .insert(12, 'F') 264 | .insert(17, 'G'); 265 | 266 | t.deepEqual(tree.keys(), [3, 5, 7, 10, 12, 15, 17]); 267 | }); 268 | 269 | test('leafNodes', t => { 270 | tree.clear() 271 | .insert(10, 'A') 272 | .insert(5, 'B') 273 | .insert(15, 'C') 274 | .insert(3, 'D') 275 | .insert(7, 'E') 276 | .insert(12, 'F') 277 | .insert(17, 'G'); 278 | 279 | t.deepEqual(tree.leafNodes().map(x => x.toPair()), [[3, 'D'], [7, 'E'], [12, 'F'], [17, 'G']]); 280 | }); 281 | 282 | test('levelOrder', t => { 283 | tree.clear() 284 | .insert(10, 'A') 285 | .insert(5, 'B') 286 | .insert(15, 'C') 287 | .insert(3, 'D') 288 | .insert(7, 'E') 289 | .insert(12, 'F') 290 | .insert(17, 'G'); 291 | 292 | const array = []; 293 | t.deepEqual(tree.levelOrder(x => array.push(x.toPair())), tree); 294 | t.deepEqual(array, [[10, 'A'], [5, 'B'], [15, 'C'], [3, 'D'], [7, 'E'], [12, 'F'], [17, 'G']]); 295 | }); 296 | 297 | test('max', t => { 298 | tree.clear() 299 | .insert(10, 'A') 300 | .insert(5, 'B') 301 | .insert(15, 'C') 302 | .insert(3, 'D') 303 | .insert(7, 'E') 304 | .insert(12, 'F') 305 | .insert(17, 'G'); 306 | 307 | const node = new Node(17, 'G'); 308 | t.deepEqual(tree.max(), node); 309 | t.deepEqual(tree.max(), tree.root.right.right); 310 | }); 311 | 312 | test('maxKey', t => { 313 | tree.clear() 314 | .insert(10, 'A') 315 | .insert(5, 'B') 316 | .insert(15, 'C') 317 | .insert(3, 'D') 318 | .insert(7, 'E') 319 | .insert(12, 'F') 320 | .insert(17, 'G'); 321 | 322 | const node = new Node(17, 'G'); 323 | t.is(tree.maxKey(), node.key); 324 | t.is(tree.maxKey(), tree.root.right.right.key); 325 | }); 326 | 327 | test('maxValue', t => { 328 | tree.clear() 329 | .insert(10, 'A') 330 | .insert(5, 'B') 331 | .insert(15, 'C') 332 | .insert(3, 'D') 333 | .insert(7, 'E') 334 | .insert(12, 'F') 335 | .insert(17, 'G'); 336 | 337 | const node = new Node(17, 'G'); 338 | t.is(tree.maxValue(), node.value); 339 | t.is(tree.maxValue(), tree.root.right.right.value); 340 | }); 341 | 342 | test('min', t => { 343 | tree.clear() 344 | .insert(10, 'A') 345 | .insert(5, 'B') 346 | .insert(15, 'C') 347 | .insert(3, 'D') 348 | .insert(7, 'E') 349 | .insert(12, 'F') 350 | .insert(17, 'G'); 351 | 352 | const node = new Node(3, 'D'); 353 | t.deepEqual(tree.min(), node); 354 | t.deepEqual(tree.min(), tree.root.left.left); 355 | }); 356 | 357 | test('minKey', t => { 358 | tree.clear() 359 | .insert(10, 'A') 360 | .insert(5, 'B') 361 | .insert(15, 'C') 362 | .insert(3, 'D') 363 | .insert(7, 'E') 364 | .insert(12, 'F') 365 | .insert(17, 'G'); 366 | 367 | const node = new Node(3, 'D'); 368 | t.is(tree.minKey(), node.key); 369 | t.is(tree.minKey(), tree.root.left.left.key); 370 | }); 371 | 372 | test('minValue', t => { 373 | tree.clear() 374 | .insert(10, 'A') 375 | .insert(5, 'B') 376 | .insert(15, 'C') 377 | .insert(3, 'D') 378 | .insert(7, 'E') 379 | .insert(12, 'F') 380 | .insert(17, 'G'); 381 | 382 | const node = new Node(3, 'D'); 383 | t.is(tree.minValue(), node.value); 384 | t.is(tree.minValue(), tree.root.left.left.value); 385 | }); 386 | 387 | test('outOrder', t => { 388 | tree.clear() 389 | .insert(10, 'A') 390 | .insert(5, 'B') 391 | .insert(15, 'C') 392 | .insert(3, 'D') 393 | .insert(7, 'E') 394 | .insert(12, 'F') 395 | .insert(17, 'G'); 396 | 397 | const array = []; 398 | t.deepEqual(tree.outOrder(x => array.push(x.toPair())), tree); 399 | t.deepEqual(array, [[17, 'G'], [15, 'C'], [12, 'F'], [10, 'A'], [7, 'E'], [5, 'B'], [3, 'D']]); 400 | }); 401 | 402 | test('postOrder', t => { 403 | tree.clear() 404 | .insert(10, 'A') 405 | .insert(5, 'B') 406 | .insert(15, 'C') 407 | .insert(3, 'D') 408 | .insert(7, 'E') 409 | .insert(12, 'F') 410 | .insert(17, 'G'); 411 | 412 | const array = []; 413 | t.deepEqual(tree.postOrder(x => array.push(x.toPair())), tree); 414 | t.deepEqual(array, [[3, 'D'], [7, 'E'], [5, 'B'], [12, 'F'], [17, 'G'], [15, 'C'], [10, 'A']]); 415 | }); 416 | 417 | test('preOrder', t => { 418 | tree.clear() 419 | .insert(10, 'A') 420 | .insert(5, 'B') 421 | .insert(15, 'C') 422 | .insert(3, 'D') 423 | .insert(7, 'E') 424 | .insert(12, 'F') 425 | .insert(17, 'G'); 426 | 427 | const array = []; 428 | t.deepEqual(tree.preOrder(x => array.push(x.toPair())), tree); 429 | t.deepEqual(array, [[10, 'A'], [5, 'B'], [3, 'D'], [7, 'E'], [15, 'C'], [12, 'F'], [17, 'G']]); 430 | }); 431 | 432 | test('remove', t => { 433 | tree.clear() 434 | .insert(10, 'A') 435 | .insert(5, 'B') 436 | .insert(15, 'C'); 437 | 438 | tree.remove(10); 439 | t.true(tree._isBalanced()); 440 | 441 | t.is(tree.root.key, 15); 442 | t.is(tree.root.value, 'C'); 443 | 444 | tree.remove(15); 445 | t.true(tree._isBalanced()); 446 | 447 | t.is(tree.root.key, 5); 448 | t.is(tree.root.value, 'B'); 449 | 450 | tree.insert(2, 'E'); 451 | t.true(tree._isBalanced()); 452 | 453 | tree.remove(5); 454 | t.true(tree._isBalanced()); 455 | 456 | t.is(tree.root.key, 2); 457 | t.is(tree.root.value, 'E'); 458 | 459 | tree.insert(3, 'F'); 460 | t.true(tree._isBalanced()); 461 | 462 | tree.remove(2); 463 | t.true(tree._isBalanced()); 464 | 465 | t.is(tree.root.key, 3); 466 | t.is(tree.root.value, 'F'); 467 | 468 | tree.remove(3); 469 | t.true(tree._isBalanced()); 470 | 471 | t.is(tree.root, null); 472 | }); 473 | 474 | test('remove LL case', t => { 475 | tree.clear() 476 | .insert(5, 'A') 477 | .insert(7, 'E') 478 | .insert(3, 'B') 479 | .insert(4, 'C') 480 | .insert(2, 'D'); 481 | 482 | tree.remove(7); 483 | t.true(tree._isBalanced()); 484 | 485 | t.is(tree.root.key, 3); 486 | t.is(tree.root.value, 'B'); 487 | t.is(tree.root.left.key, 2); 488 | t.is(tree.root.left.value, 'D'); 489 | t.is(tree.root.left.left, null); 490 | t.is(tree.root.left.right, null); 491 | t.is(tree.root.right.key, 5); 492 | t.is(tree.root.right.value, 'A'); 493 | t.is(tree.root.right.left.key, 4); 494 | t.is(tree.root.right.left.value, 'C'); 495 | t.is(tree.root.right.right, null); 496 | }); 497 | 498 | test('remove LR case', t => { 499 | tree.clear() 500 | .insert(5, 'A') 501 | .insert(7, 'E') 502 | .insert(3, 'B') 503 | .insert(4, 'C'); 504 | 505 | tree.remove(7); 506 | t.true(tree._isBalanced()); 507 | 508 | t.is(tree.root.key, 4); 509 | t.is(tree.root.value, 'C'); 510 | t.is(tree.root.left.key, 3); 511 | t.is(tree.root.left.value, 'B'); 512 | t.is(tree.root.left.left, null); 513 | t.is(tree.root.left.right, null); 514 | t.is(tree.root.right.key, 5); 515 | t.is(tree.root.right.value, 'A'); 516 | t.is(tree.root.right.left, null); 517 | t.is(tree.root.right.right, null); 518 | }); 519 | 520 | test('remove RR case', t => { 521 | tree.clear() 522 | .insert(5, 'A') 523 | .insert(3, 'B') 524 | .insert(7, 'C') 525 | .insert(6, 'D') 526 | .insert(8, 'E'); 527 | 528 | tree.remove(3); 529 | t.true(tree._isBalanced()); 530 | 531 | t.is(tree.root.key, 7); 532 | t.is(tree.root.value, 'C'); 533 | t.is(tree.root.left.key, 5); 534 | t.is(tree.root.left.value, 'A'); 535 | t.is(tree.root.left.left, null); 536 | t.is(tree.root.left.right.key, 6); 537 | t.is(tree.root.left.right.value, 'D'); 538 | t.is(tree.root.right.key, 8); 539 | t.is(tree.root.right.value, 'E'); 540 | t.is(tree.root.right.left, null); 541 | t.is(tree.root.right.right, null); 542 | }); 543 | 544 | test('remove RL case', t => { 545 | tree.clear() 546 | .insert(5, 'A') 547 | .insert(3, 'B') 548 | .insert(7, 'C') 549 | .insert(6, 'D'); 550 | 551 | tree.remove(3); 552 | t.true(tree._isBalanced()); 553 | 554 | t.is(tree.root.key, 6); 555 | t.is(tree.root.value, 'D'); 556 | t.is(tree.root.left.key, 5); 557 | t.is(tree.root.left.value, 'A'); 558 | t.is(tree.root.left.left, null); 559 | t.is(tree.root.left.right, null); 560 | t.is(tree.root.right.key, 7); 561 | t.is(tree.root.right.value, 'C'); 562 | t.is(tree.root.right.left, null); 563 | t.is(tree.root.right.right, null); 564 | }); 565 | 566 | test('search', t => { 567 | tree.clear() 568 | .insert(10, 'A') 569 | .insert(5, 'B') 570 | .insert(15, 'C') 571 | .insert(3, 'D') 572 | .insert(7, 'E') 573 | .insert(12, 'F') 574 | .insert(17, 'G'); 575 | 576 | const node = tree.search(12); 577 | t.is(node.key, 12); 578 | t.is(node.value, 'F'); 579 | t.is(node.left, null); 580 | t.is(node.right, null); 581 | }); 582 | 583 | test('size', t => { 584 | tree.clear() 585 | .insert(10, 'A') 586 | .insert(5, 'B') 587 | .insert(15, 'C') 588 | .insert(3, 'D') 589 | .insert(7, 'E') 590 | .insert(12, 'F') 591 | .insert(17, 'G'); 592 | 593 | t.is(tree.size(), 7); 594 | }); 595 | 596 | test('toArray', t => { 597 | tree.clear() 598 | .insert(10, 'A') 599 | .insert(5, 'B') 600 | .insert(15, 'C') 601 | .insert(3, 'D') 602 | .insert(7, 'E') 603 | .insert(12, 'F') 604 | .insert(17, 'G'); 605 | 606 | t.deepEqual(tree.toArray().map(x => x.toPair()), [[3, 'D'], [5, 'B'], [7, 'E'], [10, 'A'], [12, 'F'], [15, 'C'], [17, 'G']]); 607 | }); 608 | 609 | test('toPairs', t => { 610 | tree.clear() 611 | .insert(10, 'A') 612 | .insert(5, 'B') 613 | .insert(15, 'C') 614 | .insert(3, 'D') 615 | .insert(7, 'E') 616 | .insert(12, 'F') 617 | .insert(17, 'G'); 618 | 619 | t.deepEqual(tree.toPairs(), [[3, 'D'], [5, 'B'], [7, 'E'], [10, 'A'], [12, 'F'], [15, 'C'], [17, 'G']]); 620 | }); 621 | 622 | test('values', t => { 623 | tree.clear() 624 | .insert(10, 'A') 625 | .insert(5, 'B') 626 | .insert(15, 'C') 627 | .insert(3, 'D') 628 | .insert(7, 'E') 629 | .insert(12, 'F') 630 | .insert(17, 'G'); 631 | 632 | t.deepEqual(tree.values(), ['D', 'B', 'E', 'A', 'F', 'C', 'G']); 633 | }); 634 | -------------------------------------------------------------------------------- /test/node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const test = require('ava'); 3 | const {Node} = require('../.'); 4 | 5 | test('key', t => { 6 | const node = new Node(10, 'A'); 7 | t.is(node.key, 10); 8 | }); 9 | 10 | test('value', t => { 11 | const node = new Node(10, 'A'); 12 | t.is(node.value, 'A'); 13 | node.value = 'B'; 14 | t.is(node.value, 'B'); 15 | }); 16 | 17 | test('left', t => { 18 | const node = new Node(10, 'A'); 19 | t.is(node.left, null); 20 | t.throws(() => { 21 | node.left = new Node(15, 'B'); 22 | }, 'Left child node key must be less than the parent node key'); 23 | node.left = new Node(5, 'B'); 24 | t.deepEqual(node.left, new Node(5, 'B')); 25 | t.is(node.left.key, 5); 26 | t.is(node.left.value, 'B'); 27 | t.is(node.left.left, null); 28 | t.is(node.left.right, null); 29 | }); 30 | 31 | test('right', t => { 32 | const node = new Node(10, 'A'); 33 | t.is(node.right, null); 34 | t.throws(() => { 35 | node.right = new Node(5, 'B'); 36 | }, 'Right child node key must be greater than the parent node key'); 37 | node.right = new Node(15, 'B'); 38 | t.deepEqual(node.right, new Node(15, 'B')); 39 | t.is(node.right.key, 15); 40 | t.is(node.right.value, 'B'); 41 | t.is(node.right.left, null); 42 | t.is(node.right.right, null); 43 | }); 44 | 45 | test('children', t => { 46 | const node = new Node(10, 'A'); 47 | const left = new Node(5, 'B'); 48 | const right = new Node(15, 'C'); 49 | t.deepEqual(node.children, []); 50 | node.left = left; 51 | t.deepEqual(node.children, [left]); 52 | node.right = right; 53 | t.deepEqual(node.children, [left, right]); 54 | }); 55 | 56 | test('balanceFactor', t => { 57 | const node = new Node(10, 'A'); 58 | const left = new Node(5, 'B'); 59 | const right = new Node(15, 'C'); 60 | t.is(node.balanceFactor, 0); 61 | node.left = left; 62 | t.is(node.balanceFactor, 1); 63 | node.left = null; 64 | node.right = right; 65 | t.is(node.balanceFactor, -1); 66 | }); 67 | 68 | test('height', t => { 69 | const node = new Node(10, 'A'); 70 | const left = new Node(5, 'B'); 71 | const right = new Node(15, 'C'); 72 | t.is(node.height, 0); 73 | node.left = left; 74 | node.right = right; 75 | t.is(node.height, 0); 76 | }); 77 | 78 | test('degree', t => { 79 | const node = new Node(10, 'A'); 80 | const left = new Node(5, 'B'); 81 | const right = new Node(15, 'C'); 82 | t.is(node.degree, 0); 83 | node.left = left; 84 | t.is(node.degree, 1); 85 | node.right = right; 86 | t.is(node.degree, 2); 87 | }); 88 | 89 | test('isBalanced', t => { 90 | const node = new Node(10, 'A'); 91 | const left = new Node(5, 'B'); 92 | const right = new Node(15, 'C'); 93 | t.true(node.isBalanced()); 94 | node.left = left; 95 | t.false(node.isBalanced()); 96 | node.right = right; 97 | t.true(node.isBalanced()); 98 | }); 99 | 100 | test('isFull', t => { 101 | const node = new Node(10, 'A'); 102 | const left = new Node(5, 'B'); 103 | const right = new Node(15, 'C'); 104 | t.false(node.isFull()); 105 | node.left = left; 106 | t.false(node.isFull()); 107 | node.right = right; 108 | t.true(node.isFull()); 109 | }); 110 | 111 | test('isInternal', t => { 112 | const node = new Node(10, 'A'); 113 | const left = new Node(5, 'B'); 114 | const right = new Node(15, 'C'); 115 | t.false(node.isInternal()); 116 | node.left = left; 117 | t.true(node.isInternal()); 118 | node.right = right; 119 | t.true(node.isInternal()); 120 | }); 121 | 122 | test('isLeaf', t => { 123 | const node = new Node(10, 'A'); 124 | const left = new Node(5, 'B'); 125 | const right = new Node(15, 'C'); 126 | t.true(node.isLeaf()); 127 | node.left = left; 128 | t.false(node.isLeaf()); 129 | node.right = right; 130 | t.false(node.isLeaf()); 131 | }); 132 | 133 | test('isLeftHeavy', t => { 134 | const node = new Node(10, 'A'); 135 | const left = new Node(5, 'B'); 136 | const right = new Node(15, 'C'); 137 | t.false(node.isLeftHeavy()); 138 | node.left = left; 139 | t.true(node.isLeftHeavy()); 140 | node.left = null; 141 | node.right = right; 142 | t.false(node.isLeftHeavy()); 143 | }); 144 | 145 | test('isLeftPartial', t => { 146 | const node = new Node(10, 'A'); 147 | const left = new Node(5, 'B'); 148 | const right = new Node(15, 'C'); 149 | t.false(node.isLeftPartial()); 150 | node.left = left; 151 | t.true(node.isLeftPartial()); 152 | node.right = right; 153 | t.false(node.isLeftPartial()); 154 | }); 155 | 156 | test('isPartial', t => { 157 | const node = new Node(10, 'A'); 158 | const left = new Node(5, 'B'); 159 | const right = new Node(15, 'C'); 160 | t.false(node.isPartial()); 161 | node.left = left; 162 | t.true(node.isPartial()); 163 | node.right = right; 164 | t.false(node.isPartial()); 165 | }); 166 | 167 | test('isRightHeavy', t => { 168 | const node = new Node(10, 'A'); 169 | const left = new Node(5, 'B'); 170 | const right = new Node(15, 'C'); 171 | t.false(node.isRightHeavy()); 172 | node.left = left; 173 | t.false(node.isRightHeavy()); 174 | node.left = null; 175 | node.right = right; 176 | t.true(node.isRightHeavy()); 177 | }); 178 | 179 | test('isRightPartial', t => { 180 | const node = new Node(10, 'A'); 181 | const left = new Node(5, 'B'); 182 | const right = new Node(15, 'C'); 183 | t.false(node.isRightPartial()); 184 | node.right = right; 185 | t.true(node.isRightPartial()); 186 | node.left = left; 187 | t.false(node.isRightPartial()); 188 | }); 189 | 190 | test('leftChildHeight', t => { 191 | const node = new Node(10, 'A'); 192 | const left = new Node(5, 'B'); 193 | t.is(node.leftChildHeight(), -1); 194 | node.left = left; 195 | t.is(node.leftChildHeight(), 0); 196 | }); 197 | 198 | test('maxChildHeight', t => { 199 | const node = new Node(10, 'A'); 200 | const left = new Node(5, 'B'); 201 | t.is(node.maxChildHeight(), -1); 202 | node.left = left; 203 | t.is(node.maxChildHeight(), 0); 204 | }); 205 | 206 | test('rightChildHeight', t => { 207 | const node = new Node(10, 'A'); 208 | const right = new Node(15, 'C'); 209 | t.is(node.rightChildHeight(), -1); 210 | node.right = right; 211 | t.is(node.rightChildHeight(), 0); 212 | }); 213 | 214 | test('toPair', t => { 215 | const node = new Node(10, 'A'); 216 | t.deepEqual(node.toPair(), [10, 'A']); 217 | }); 218 | -------------------------------------------------------------------------------- /test/single.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const test = require('ava'); 3 | const {Node, Tree} = require('../.'); 4 | 5 | const tree = new Tree(); 6 | 7 | test('insert', t => { 8 | const node = new Node(10, 'A'); 9 | tree.insert(10, 'A'); 10 | t.deepEqual(tree.root, node); 11 | t.is(tree.root.key, 10); 12 | t.is(tree.root.value, 'A'); 13 | t.is(tree.root.left, null); 14 | t.is(tree.root.right, null); 15 | }); 16 | 17 | test('root', t => { 18 | t.deepEqual(tree.root, new Node(10, 'A')); 19 | }); 20 | 21 | test('clear', t => { 22 | t.deepEqual(tree.clear(), new Tree()); 23 | t.is(tree.root, null); 24 | }); 25 | 26 | test('fullNodes', t => { 27 | tree.insert(10, 'A'); 28 | t.deepEqual(tree.fullNodes(), []); 29 | }); 30 | 31 | test('height', t => { 32 | t.is(tree.height(), 0); 33 | }); 34 | 35 | test('includes', t => { 36 | t.false(tree.includes(5)); 37 | t.true(tree.includes(10)); 38 | }); 39 | 40 | test('inOrder', t => { 41 | const array = []; 42 | const node = new Node(10, 'A'); 43 | t.deepEqual(tree.inOrder(x => array.push(x)), tree); 44 | t.deepEqual(array, [node]); 45 | t.deepEqual(array, [tree.root]); 46 | }); 47 | 48 | test('internalNodes', t => { 49 | t.deepEqual(tree.internalNodes(), []); 50 | }); 51 | 52 | test('isComplete', t => { 53 | t.true(tree.isComplete()); 54 | }); 55 | 56 | test('isEmpty', t => { 57 | t.false(tree.isEmpty()); 58 | }); 59 | 60 | test('isFull', t => { 61 | t.true(tree.isFull()); 62 | }); 63 | 64 | test('isPerfect', t => { 65 | t.true(tree.isPerfect()); 66 | }); 67 | 68 | test('keys', t => { 69 | t.deepEqual(tree.keys(), [10]); 70 | }); 71 | 72 | test('leafNodes', t => { 73 | const node = new Node(10, 'A'); 74 | const leaves = tree.leafNodes(); 75 | t.deepEqual(leaves, [node]); 76 | t.deepEqual(leaves, [tree.root]); 77 | }); 78 | 79 | test('levelOrder', t => { 80 | const array = []; 81 | const node = new Node(10, 'A'); 82 | t.deepEqual(tree.levelOrder(x => array.push(x)), tree); 83 | t.deepEqual(array, [node]); 84 | t.deepEqual(array, [tree.root]); 85 | }); 86 | 87 | test('max', t => { 88 | const node = new Node(10, 'A'); 89 | t.deepEqual(tree.max(), node); 90 | t.deepEqual(tree.max(), tree.root); 91 | }); 92 | 93 | test('maxKey', t => { 94 | t.is(tree.maxKey(), 10); 95 | }); 96 | 97 | test('maxValue', t => { 98 | t.is(tree.maxValue(), 'A'); 99 | }); 100 | 101 | test('min', t => { 102 | t.deepEqual(tree.min(), new Node(10, 'A')); 103 | t.deepEqual(tree.min(), tree.root); 104 | }); 105 | 106 | test('minKey', t => { 107 | t.is(tree.minKey(), 10); 108 | }); 109 | 110 | test('minValue', t => { 111 | t.is(tree.minValue(), 'A'); 112 | }); 113 | 114 | test('outOrder', t => { 115 | const array = []; 116 | const node = new Node(10, 'A'); 117 | t.deepEqual(tree.outOrder(x => array.push(x)), tree); 118 | t.deepEqual(array, [node]); 119 | t.deepEqual(array, [tree.root]); 120 | }); 121 | 122 | test('postOrder', t => { 123 | const array = []; 124 | const node = new Node(10, 'A'); 125 | t.deepEqual(tree.postOrder(x => array.push(x)), tree); 126 | t.deepEqual(array, [node]); 127 | t.deepEqual(array, [tree.root]); 128 | }); 129 | 130 | test('preOrder', t => { 131 | const array = []; 132 | const node = new Node(10, 'A'); 133 | t.deepEqual(tree.preOrder(x => array.push(x)), tree); 134 | t.deepEqual(array, [node]); 135 | t.deepEqual(array, [tree.root]); 136 | }); 137 | 138 | test('remove', t => { 139 | t.deepEqual(tree.remove(10), tree); 140 | t.deepEqual(tree.remove(10), new Tree()); 141 | t.is(tree.root, null); 142 | }); 143 | 144 | test('search', t => { 145 | tree.insert(10, 'A'); 146 | const node = new Node(10, 'A'); 147 | t.deepEqual(tree.search(10), tree.root); 148 | t.deepEqual(tree.search(10), node); 149 | }); 150 | 151 | test('size', t => { 152 | t.is(tree.size(), 1); 153 | }); 154 | 155 | test('toArray', t => { 156 | const node = new Node(10, 'A'); 157 | t.deepEqual(tree.toArray(), [tree.root]); 158 | t.deepEqual(tree.toArray(), [node]); 159 | }); 160 | 161 | test('toPairs', t => { 162 | const node = new Node(10, 'A'); 163 | t.deepEqual(tree.toPairs(), [[10, 'A']]); 164 | t.deepEqual(tree.toPairs(), [node.toPair()]); 165 | t.deepEqual(tree.toPairs(), [tree.root.toPair()]); 166 | }); 167 | 168 | test('values', t => { 169 | t.deepEqual(tree.values(), ['A']); 170 | }); 171 | -------------------------------------------------------------------------------- /test/types/avlbinstree.ts: -------------------------------------------------------------------------------- 1 | import { Tree, Node } from '../..'; 2 | 3 | const tree = new Tree(); 4 | //=> Tree { root: null } 5 | 6 | tree.insert(9, 'A'); 7 | // => Tree { root: Node { left: null, right: null, key: 9, value: 'A' } } 8 | 9 | tree.root; 10 | //=> Node { left: null, right: null, key: 10, value: 'A' } 11 | 12 | const node = new Node(9, 'A'); 13 | 14 | tree.root.key === node.key; 15 | //=> true 16 | 17 | tree.root.value === node.value; 18 | //=> true 19 | 20 | tree.insert(5, 'B').insert(13, 'C').root; 21 | //=> Node { left: [Node], right: [Node], key: 9, value: 'A' } 22 | 23 | tree.root.left; 24 | //=> Node { left: null, right: null, key: 5, value: 'B' } 25 | 26 | tree.root.right; 27 | //=> Node { left: null, right: null, key: 13, value: 'C' } 28 | 29 | tree.insert(11, 'D').insert(15, 'E'); 30 | /*=> {9} 31 | * / \ 32 | * {5} {13} 33 | * / \ 34 | * {11} {15} 35 | */ 36 | 37 | tree.size(); 38 | //=> 5 39 | 40 | tree.search(13); 41 | //=> Node { key: 13, value: 'C', 42 | // left: Node { left: null, right: null, key: 11, value: 'D' }, 43 | // right: Node { left: null, right: null, key: 15, value: 'E' } } 44 | 45 | tree.search(25); 46 | //=> null 47 | 48 | tree.includes(11); 49 | //=> true 50 | 51 | tree.includes(100); 52 | //=> false 53 | 54 | tree.height(); 55 | //=> 2 56 | 57 | tree.remove(5); 58 | /*=> {13} 59 | * / \ 60 | * {9} {15} 61 | * \ 62 | * {11} 63 | */ 64 | 65 | tree.root.isRightHeavy(); 66 | //=> false 67 | 68 | tree.root.isLeftHeavy(); 69 | //=> true 70 | 71 | tree.max(); 72 | //=> Node { left: null, right: null, key: 15, value: 'E' } 73 | 74 | tree.maxKey(); 75 | //=> 15 76 | 77 | tree.maxValue(); 78 | //=> 'E' 79 | 80 | tree.min(); 81 | //=> Node { left: null, right: null, key: 9, value: 'A' } 82 | 83 | tree.minKey(); 84 | //=> 9 85 | 86 | tree.minValue(); 87 | //=> 'A' 88 | 89 | tree.remove(15); 90 | /*=> {11} 91 | * / \ 92 | * {9} {13} 93 | */ 94 | 95 | tree.root.isBalanced(); 96 | //=> true 97 | 98 | tree.keys(); 99 | //=> [9, 11, 13] 100 | 101 | tree.values(); 102 | //=> ['A', 'D', 'C'] 103 | -------------------------------------------------------------------------------- /test/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "strict": true, 5 | "strictNullChecks": false, 6 | "target": "ES2017" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /types/avlbinstree.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace node { 2 | export interface Constructor { 3 | new (key: number, value: T): Instance; 4 | } 5 | 6 | export interface Instance { 7 | value: T; 8 | left: Instance | null; 9 | right: Instance | null; 10 | readonly balanceFactor: number; 11 | readonly height: number; 12 | readonly key: number; 13 | readonly children: Instance[]; 14 | readonly degree: 0 | 1 | 2; 15 | isBalanced(): boolean; 16 | isFull(): boolean; 17 | isInternal(): boolean; 18 | isLeaf(): boolean; 19 | isLeftHeavy(): boolean; 20 | isLeftPartial(): boolean; 21 | isPartial(): boolean; 22 | isRightHeavy(): boolean; 23 | isRightPartial(): boolean; 24 | leftChildHeight(): number; 25 | maxChildHeight(): number; 26 | rightChildHeight(): number; 27 | toPair(): [number, T]; 28 | } 29 | } 30 | 31 | declare namespace tree { 32 | type UnaryCallback = (x: T) => void; 33 | 34 | interface Node extends node.Instance {} 35 | 36 | export interface Constructor { 37 | new (): Instance; 38 | } 39 | 40 | export interface Instance { 41 | readonly root: Node | null; 42 | clear(): this; 43 | fullNodes(): Node[]; 44 | height(): number; 45 | includes(key: number): boolean; 46 | inOrder(fn: UnaryCallback>): this; 47 | insert(key: number, value: T): this; 48 | internalNodes(): Node[]; 49 | isComplete(): boolean; 50 | isEmpty(): boolean; 51 | isFull(): boolean; 52 | isPerfect(): boolean; 53 | keys(): number[]; 54 | leafNodes(): Node[]; 55 | levelOrder(fn: UnaryCallback>): this; 56 | max(): Node | null; 57 | maxKey(): number | null; 58 | maxValue(): T | null; 59 | min(): Node | null; 60 | minKey(): number | null; 61 | minValue(): T | null; 62 | outOrder(fn: UnaryCallback>): this; 63 | postOrder(fn: UnaryCallback>): this; 64 | preOrder(fn: UnaryCallback>): this; 65 | remove(key: number): this; 66 | search(key: number): Node | null; 67 | size(): number; 68 | toArray(): Node[]; 69 | toPairs(): [number, T][]; 70 | values(): T[]; 71 | } 72 | } 73 | 74 | declare namespace avlbinstree { 75 | export interface Node extends node.Instance {} 76 | export interface Tree extends tree.Instance {} 77 | } 78 | 79 | declare const avlbinstree: { 80 | Node: node.Constructor; 81 | Tree: tree.Constructor; 82 | }; 83 | 84 | export = avlbinstree; 85 | --------------------------------------------------------------------------------