diff --git a/.eslintrc b/.eslintrc index f7496f8..4b4c3a3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,10 +1,10 @@ { "rules": { - max-len: ["error", { "code": 80, "ignoreComments": true }], + "max-len": ["error", { "code": 120, "ignoreComments": true }], "comma-dangle": ["error", { "functions": "ignore" }], - no-underscore-dangle: [ + "no-underscore-dangle": [ "error", { "allowAfterThis": true } ] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 32b2ca6..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: node_js -node_js: - - "8" - - "9" - - "10" - - "11" - - "12" -install: - - npm install -g grunt-cli - - npm install -script: - - grunt build diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ca8216..fd7983c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [5.0.0] - 2022-07-19 + +### Changed +- tree now accepts a compare function. + ## [4.3.2] - 2022-07-13 ### Fixed diff --git a/README.md b/README.md index d9e6147..2442000 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # @datastructures-js/binary-search-tree -[![build:?](https://travis-ci.org/datastructures-js/binary-search-tree.svg?branch=master)](https://travis-ci.org/datastructures-js/binary-search-tree) [![npm](https://img.shields.io/npm/v/@datastructures-js/binary-search-tree.svg)](https://www.npmjs.com/package/@datastructures-js/binary-search-tree) [![npm](https://img.shields.io/npm/dm/@datastructures-js/binary-search-tree.svg)](https://www.npmjs.com/package/@datastructures-js/binary-search-tree) [![npm](https://img.shields.io/badge/node-%3E=%206.0-blue.svg)](https://www.npmjs.com/package/@datastructures-js/binary-search-tree) @@ -62,109 +61,150 @@ import { ## API ### constructor +constructor accepts a custom compare function to insert new values into the tree based on the returned number: + +the compare function must return a number for the 3 cases: +* less than 0 to place a value on the left. +* greater than 0 to place a value on the right. +* 0 for equal values. + +There is already a default compare function for primitive values (number, string). ##### JS +###### BinarySearchTree ```js -const bst = new BinarySearchTree(); +const nums = new BinarySearchTree(); +const employees = new BinarySearchTree((a, b) => a.id - b.id); ``` +###### AvlTree ```js -// self balancing tree -const bst = new AvlTree(); +const nums = new AvlTree(); +const employees = new AvlTree((a, b) => a.id - b.id); ``` ##### TS ```js -// BinarySearchTree -const bst = new BinarySearchTree(); +interface IEmployee { + id: number; +} +``` + +###### BinarySearchTree +```js +const nums = new BinarySearchTree(); +const employees = new BinarySearchTree((a, b) => a.id - b.id); ``` +###### AvlTree ```js -// AvlTree -const bst = new AvlTree(); +const nums = new AvlTree(); +const employees = new AvlTree((a, b) => a.id - b.id); ``` ### insert O(log(n)) -inserts a node with key/value into the tree and returns the inserted node. Inserting an node with existing key, will update the existing node's value with the new one. +inserts a value into the tree and returns the inserted node. Inserting an node with existing value, will update the existing node's value with the new one. ```js -bst.insert(50, 'v1'); -bst.insert(80, 'v2'); -bst.insert(30, 'v3'); -bst.insert(90, 'v4'); -bst.insert(60, 'v5'); -bst.insert(40, 'v6'); -bst.insert(20, 'v7'); +nums + .insert(50) + .insert(80) + .insert(30) + .insert(90) + .insert(60) + .insert(40) + .insert(20); + +employees + .insert({ id: 50 }) + .insert({ id: 80 }) + .insert({ id: 30 }) + .insert({ id: 90 }) + .insert({ id: 60 }) + .insert({ id: 40 }) + .insert({ id: 20 }); ``` ### has O(log(n)) -checks if a node exists by its key. +checks if a value exists. ```js -bst.has(50); // true -bst.has(100); // false +nums.has(50); // true +nums.has(100); // false + +employees.has({ id: 50 }); // true +employees.has({ id: 100 }); // false ``` ### find O(log(n)) -finds a node in the tree by its key. +finds a value and returns its node. ```js -const n60 = bst.find(60); -console.log(n60.getKey()); // 60 -console.log(n60.getValue()); // v5 +nums.find(60).getValue(); // 60 +nums.find(100); // null -console.log(bst.find(100)); // null +employees.find({ id: 60 }).getValue(); // { id: 60 } +employees.find({ id: 100 }); // null ``` ### min O(log(n)) -finds the node with min key in the tree. +finds the node with min value in the tree. ```js -const min = bst.min(); -console.log(min.getKey()); // 20 -console.log(min.getValue()); // v7 +nums.min().getValue(); // 20 + +employees.min().getValue(); // { id: 20 } ``` ### max O(log(n)) -finds the node with max key in the tree. +finds the node with max value in the tree. ```js -const max = bst.max(); -console.log(max.getKey()); // 90 -console.log(max.getValue()); // v4 +nums.max().getValue(); // 90 + +employees.max().getValue(); // { id: 90 } ``` ### lowerBound (floor) O(log(n)) -finds the node with the biggest key less or equal a given key k. You can eliminate equal keys by passing second param as false. `.floor` is an alias to the same function. +finds the node with the biggest value less or equal a given value. You can eliminate equal values by passing second param as false. `.floor` is an alias to the same function. ```js -console.log(bst.lowerBound(60).getKey()); // 60 -console.log(bst.lowerBound(60, false).getKey()); // 50 -console.log(bst.lowerBound(10)); // null +nums.lowerBound(60).getValue(); // 60 +nums.lowerBound(60, false).getValue(); // 50 +nums.lowerBound(10); // null + +employees.floor({ id: 60 }).getValue(); // { id: 60 } +employees.floor({ id: 60 }, false).getValue(); // { id: 50 } +employees.floor({ id: 10 }); // null ``` ### upperBound (ceil) O(log(n)) -finds the node with the smallest key bigger or equal a given key k. You can eliminate equal keys by passing second param as false. `.ceil` is an alias to the same function. +finds the node with the smallest value bigger or equal a given value. You can eliminate equal values by passing second param as false. `.ceil` is an alias to the same function. ```js -console.log(bst.upperBound(75).getKey()); // 80 -console.log(bst.upperBound(80).getKey()); // 80 -console.log(bst.upperBound(80, false).getKey()); // 90 -console.log(bst.upperBound(110)); // null +nums.upperBound(75).getValue(); // 80 +nums.upperBound(80).getValue(); // 80 +nums.upperBound(80, false).getValue(); // 90 +nums.upperBound(110); // null + +employees.ceil({ id: 75 }).getValue(); // { id: 80 } +employees.ceil({ id: 80 }).getValue(); // { id: 80 } +employees.ceil({ id: 80 }, false).getValue(); // { id: 90 } +employees.ceil({ id: 110 }); // null ``` ### root @@ -173,9 +213,9 @@ O(1) returns the root node of the tree. ```js -const root = bst.root(); -console.log(root.getKey()); // 50 -console.log(root.getValue()); // v1 +nums.root().getValue(); // 50 + +employees.root().getValue(); // { id: 50 } ``` ### count @@ -184,7 +224,9 @@ O(1) returns the count of nodes in the tree. ```js -console.log(bst.count()); // 7 +nums.count(); // 7 + +employees.count(); // 7 ``` ### traverseInOrder @@ -193,8 +235,7 @@ O(n) traverses the tree in order (left-node-right). ```js -bst.traverseInOrder((node) => console.log(node.getKey())); - +nums.traverseInOrder((node) => console.log(node.getValue())); /* 20 30 @@ -204,6 +245,17 @@ bst.traverseInOrder((node) => console.log(node.getKey())); 80 90 */ + +employees.traverseInOrder((node) => console.log(node.getValue())); +/* + { id: 20 } + { id: 30 } + { id: 40 } + { id: 50 } + { id: 60 } + { id: 80 } + { id: 90 } +*/ ``` ### traversePreOrder @@ -212,8 +264,7 @@ O(n) traverses the tree pre order (node-left-right). ```js -bst.traversePreOrder((node) => console.log(node.getKey())); - +nums.traversePreOrder((node) => console.log(node.getValue())); /* 50 30 @@ -223,6 +274,17 @@ bst.traversePreOrder((node) => console.log(node.getKey())); 60 90 */ + +employees.traversePreOrder((node) => console.log(node.getValue())); +/* + { id: 50 } + { id: 30 } + { id: 20 } + { id: 40 } + { id: 80 } + { id: 60 } + { id: 90 } +*/ ``` ### traversePostOrder @@ -231,8 +293,7 @@ O(n) traverses the tree post order (left-right-node). ```js -bst.traversePostOrder((node) => console.log(node.getKey())); - +nums.traversePostOrder((node) => console.log(node.getValue())); /* 20 40 @@ -242,17 +303,32 @@ bst.traversePostOrder((node) => console.log(node.getKey())); 80 50 */ + +employees.traversePostOrder((node) => console.log(node.getValue())); +/* + { id: 20 } + { id: 40 } + { id: 30 } + { id: 60 } + { id: 90 } + { id: 80 } + { id: 50 } +*/ ``` ### remove O(log(n)) -removes a node from the tree by its key. AVL tree will rotate nodes properly if the tree becomes unbalanced during deletion. +removes a node from the tree by its value. AVL tree will rotate nodes properly if the tree becomes unbalanced during deletion. ```js -bst.remove(20); // true -bst.remove(100); // false -console.log(bst.count()); // 6 +nums.remove(20); // true +nums.remove(100); // false +nums.count(); // 6 + +employees.remove({ id: 20 }); // true +employees.remove({ id: 100 }); // false +employees.count(); // 6 ``` ### clear @@ -261,19 +337,57 @@ O(1) clears the tree. ```js -bst.clear(); -console.log(bst.count()); // 0 -console.log(bst.root()); // null +nums.clear(); +nums.count(); // 0 +nums.root(); // null + +employees.clear(); +employees.count(); // 0 +employees.root(); // null ``` -### BinarySearchTreeNode<T, U> +### BinarySearchTreeNode<T> + +#### setValue +sets the node's value. -#### setKey -sets the node's key. +#### getValue +gets the node's value. -#### getKey -gets the node's key. +#### setLeft +sets the node's left child. +#### getLeft +gets the node's left child. + +#### hasLeft +checks if node has a left child. + +#### setRight +sets the node's right child. + +#### getRight +gets the node's right child. + +#### hasRight +checks if node has a right child. + +#### setParent +sets the node's parent node. + +#### getParent +gets the node's parent node. + +#### hasParent +checks if node has a parent node. + +#### isLeaf +checks if node is a leaf in the tree. + +#### isRoot +check if node is the root node. + +### AvlTreeNode<T> #### setValue sets the node's value. @@ -313,9 +427,6 @@ checks if node is a leaf in the tree. #### isRoot check if node is the root node. -### AvlTreeNode<T, U> -extends BinarySearchTreeNode<T, U> and adds the following methods: - #### rotateLeft Rotates self left (counter-clockwise). diff --git a/package.json b/package.json index 8a2ea98..4c491bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@datastructures-js/binary-search-tree", - "version": "4.3.2", + "version": "5.0.0", "description": "binary search tree & avl tree (self balancing tree) implementation in javascript", "main": "index.js", "scripts": { diff --git a/src/avlTree.js b/src/avlTree.js index 8e963ad..3318ec9 100644 --- a/src/avlTree.js +++ b/src/avlTree.js @@ -12,6 +12,14 @@ const { AvlTreeNode } = require('./avlTreeNode'); * @extends BinarySearchTree */ class AvlTree extends BinarySearchTree { + constructor(compare) { + if (compare && typeof compare !== 'function') { + throw new Error('AvlTree constructor expects a compare function'); + } + + super(compare); + } + /** * Applies the proper rotation on a node * @private @@ -44,18 +52,18 @@ class AvlTree extends BinarySearchTree { } /** - * Inserts a node with a key/value into tree - * and maintains the tree balanced by applying the necessary rotations + * Inserts a value into the tree and maintains + * the tree balanced by making the necessary rotations * * @public - * @param {number|string} key - * @param {any} value + * @param {number|string|object} value * @return {AvlTree} */ - insert(key, value) { - const newNode = new AvlTreeNode(key, value); + insert(value) { + const newNode = new AvlTreeNode(value, this._compare); const insertRecursive = (current) => { - if (key < current.getKey()) { + const compare = this._compare(value, current.getValue()); + if (compare < 0) { if (current.hasLeft()) { insertRecursive(current.getLeft()); this._balanceNode(current); // backward-tracking @@ -64,7 +72,7 @@ class AvlTree extends BinarySearchTree { current.setLeft(newNode).updateHeight(); this._count += 1; } - } else if (key > current.getKey()) { + } else if (compare > 0) { if (current.hasRight()) { insertRecursive(current.getRight()); this._balanceNode(current); // backward-tracking @@ -85,31 +93,32 @@ class AvlTree extends BinarySearchTree { insertRecursive(this._root); } - return newNode; + return this; } /** - * Removes a node by its key - * and maintains the tree balanced by applying the necessary rotations + * Removes a node from the tree and maintains + * the tree balanced by making the necessary rotations * * @public - * @param {number|string} key + * @param {number|string|object} value * @return {boolean} */ - remove(key) { - const removeRecursively = (k, current) => { + remove(value) { + const removeRecursively = (val, current) => { if (current === null) { return false; } - if (k < current.getKey()) { - const removed = removeRecursively(k, current.getLeft()); + const compare = this._compare(val, current.getValue()); + if (compare < 0) { + const removed = removeRecursively(val, current.getLeft()); this._balanceNode(current); return removed; } - if (k > current.getKey()) { - const removed = removeRecursively(k, current.getRight()); + if (compare > 0) { + const removed = removeRecursively(val, current.getRight()); this._balanceNode(current); return removed; } @@ -120,7 +129,7 @@ class AvlTree extends BinarySearchTree { if (current.isLeaf()) { if (current.isRoot()) { this._root = null; - } else if (k < current.getParent().getKey()) { + } else if (this._compare(val, current.getParent().getValue()) < 0) { current.getParent().setLeft(null).updateHeight(); } else { current.getParent().setRight(null).updateHeight(); @@ -133,7 +142,7 @@ class AvlTree extends BinarySearchTree { if (!current.hasRight()) { if (current.isRoot()) { this._root = current.getLeft(); - } else if (k < current.getParent().getKey()) { + } else if (this._compare(val, current.getParent().getValue() < 0)) { current.getParent().setLeft(current.getLeft()).updateHeight(); } else { current.getParent().setRight(current.getLeft()).updateHeight(); @@ -147,7 +156,7 @@ class AvlTree extends BinarySearchTree { if (!current.hasLeft()) { if (current.isRoot()) { this._root = current.getRight(); - } else if (k < current.getParent().getKey()) { + } else if (this._compare(val, current.getParent().getValue() < 0)) { current.getParent().setLeft(current.getRight()).updateHeight(); } else { current.getParent().setRight(current.getRight()).updateHeight(); @@ -159,11 +168,11 @@ class AvlTree extends BinarySearchTree { // case 4: node has left and right children const minRight = this.min(current.getRight()); - current.setKey(minRight.getKey()).setValue(minRight.getValue()); - return removeRecursively(minRight.getKey(), minRight); + current.setValue(minRight.getValue()).setValue(minRight.getValue()); + return removeRecursively(minRight.getValue(), minRight); }; - return removeRecursively(key, this._root); + return removeRecursively(value, this._root); } } diff --git a/src/avlTreeNode.js b/src/avlTreeNode.js index 3574da3..73bd42d 100644 --- a/src/avlTreeNode.js +++ b/src/avlTreeNode.js @@ -4,18 +4,144 @@ * @license MIT */ -const { BinarySearchTreeNode } = require('./binarySearchTreeNode'); - /** + * AvlTree node class type * @class AvlTreeNode - * @extends BinarySearchTreeNode */ -class AvlTreeNode extends BinarySearchTreeNode { - constructor(key, value) { - super(key, value); +class AvlTreeNode { + constructor(value, compare) { + this._value = value; + this._compare = compare; + this._left = null; + this._right = null; + this._parent = null; this._height = 1; } + /** + * @public + * @param {number|string|object} value + * @returns {AvlTreeNode} + */ + setValue(value) { + this._value = value; + return this; + } + + /** + * @public + * @return {number|string|object} + */ + getValue() { + return this._value; + } + + /** + * @public + * @param {AvlTreeNode} left + * @returns {AvlTreeNode} + */ + setLeft(left) { + if (left && !(left instanceof AvlTreeNode)) { + throw new Error('setLeft expects an AvlTreeNode'); + } + + this._left = left || null; + return this; + } + + /** + * @public + * @return {AvlTreeNode} + */ + getLeft() { + return this._left; + } + + /** + * @public + * @return {boolean} + */ + hasLeft() { + return this._left instanceof AvlTreeNode; + } + + /** + * @public + * @param {AvlTreeNode} right + * @returns {AvlTreeNode} + */ + setRight(right) { + if (right && !(right instanceof AvlTreeNode)) { + throw new Error('setRight expects a AvlTreeNode or null'); + } + + this._right = right || null; + return this; + } + + /** + * @public + * @return {AvlTreeNode} + */ + getRight() { + return this._right; + } + + /** + * @public + * @return {boolean} + */ + hasRight() { + return this._right instanceof AvlTreeNode; + } + + /** + * @public + * @param {AvlTreeNode} parent + * @returns {AvlTreeNode} + */ + setParent(parent) { + if (parent && !(parent instanceof AvlTreeNode)) { + throw new Error('setParent expects an AvlTreeNode'); + } + + this._parent = parent || null; + return this; + } + + /** + * @public + * @return {AvlTreeNode} + */ + getParent() { + return this._parent; + } + + /** + * @public + * @return {boolean} + */ + hasParent() { + return this._parent instanceof AvlTreeNode; + } + + /** + * @public + * @return {boolean} + */ + isRoot() { + return this._parent === null; + } + + /** + * @public + * @return {boolean} + */ + isLeaf() { + return !this.hasLeft() && !this.hasRight(); + } + /** * Rotate-self left (counter-clockwise) * @public @@ -39,7 +165,7 @@ class AvlTreeNode extends BinarySearchTreeNode { // rebase parent's child to node's right child if (this.hasParent() && right !== null) { - if (this._parent.getKey() < right.getKey()) { + if (this._compare(this._parent.getValue(), right.getValue()) < 0) { this._parent.setRight(right); } else { this._parent.setLeft(right); @@ -80,7 +206,7 @@ class AvlTreeNode extends BinarySearchTreeNode { // rebase parent's child to node's left child if (this.hasParent() && left !== null) { - if (this._parent.getKey() > left.getKey()) { + if (this._compare(this._parent.getValue(), left.getValue()) > 0) { this._parent.setLeft(left); } else { this._parent.setRight(left); diff --git a/src/binarySearchTree.js b/src/binarySearchTree.js index 586dd7e..3e10ed0 100644 --- a/src/binarySearchTree.js +++ b/src/binarySearchTree.js @@ -6,11 +6,21 @@ const { BinarySearchTreeNode } = require('./binarySearchTreeNode'); +const defaultCompare = (a, b) => { + if (a === b) return 0; + return a > b ? 1 : -1; +}; + /** * @class BinarySearchTree */ class BinarySearchTree { - constructor() { + constructor(compare) { + if (compare && typeof compare !== 'function') { + throw new Error('BinarySearchTree constructor expects a compare function'); + } + + this._compare = compare || defaultCompare; this._root = null; this._count = 0; } @@ -18,21 +28,21 @@ class BinarySearchTree { /** * Inserts a node with a key/value into the tree * @public - * @param {number|string} key - * @param {any} value + * @param {number|string|object} value * @return {BinarySearchTree} */ - insert(key, value) { - const newNode = new BinarySearchTreeNode(key, value); + insert(value) { + const newNode = new BinarySearchTreeNode(value); const insertRecursive = (current) => { - if (key < current.getKey()) { + const compare = this._compare(newNode.getValue(), current.getValue()); + if (compare < 0) { if (current.hasLeft()) { insertRecursive(current.getLeft()); } else { current.setLeft(newNode.setParent(current)); this._count += 1; } - } else if (key > current.getKey()) { + } else if (compare > 0) { if (current.hasRight()) { insertRecursive(current.getRight()); } else { @@ -51,7 +61,7 @@ class BinarySearchTree { insertRecursive(this._root); } - return newNode; + return this; } /** @@ -60,20 +70,13 @@ class BinarySearchTree { * @param {number|string} key * @return {boolean} */ - has(key) { + has(value) { const hasRecursive = (current) => { - if (current === null) { - return false; - } - - if (key === current.getKey()) { - return true; - } - - if (key < current.getKey()) { - return hasRecursive(current.getLeft()); - } + if (current === null) return false; + const compare = this._compare(value, current.getValue()); + if (compare === 0) return true; + if (compare < 0) return hasRecursive(current.getLeft()); return hasRecursive(current.getRight()); }; @@ -86,20 +89,13 @@ class BinarySearchTree { * @param {number|string} key * @return {BinarySearchTreeNode} */ - find(key) { + find(value) { const findRecursive = (current) => { - if (current === null) { - return null; - } - - if (key === current.getKey()) { - return current; - } - - if (key < current.getKey()) { - return findRecursive(current.getLeft()); - } + if (current === null) return null; + const compare = this._compare(value, current.getValue()); + if (compare === 0) return current; + if (compare < 0) return findRecursive(current.getLeft()); return findRecursive(current.getRight()); }; @@ -113,14 +109,8 @@ class BinarySearchTree { * @return {BinarySearchTreeNode} */ max(current = this._root) { - if (current === null) { - return null; - } - - if (current.hasRight()) { - return this.max(current.getRight()); - } - + if (current === null) return null; + if (current.hasRight()) return this.max(current.getRight()); return current; } @@ -131,35 +121,27 @@ class BinarySearchTree { * @return {BinarySearchTreeNode} */ min(current = this._root) { - if (current === null) { - return null; - } - - if (current.hasLeft()) { - return this.min(current.getLeft()); - } - + if (current === null) return null; + if (current.hasLeft()) return this.min(current.getLeft()); return current; } /** * Returns the node with the biggest key less or equal to k * @public - * @param {number|string} k + * @param {number|string|object} value * @param {boolean} includeEqual * @return {BinarySearchTreeNode|null} */ - lowerBound(k, includeEqual = true) { + lowerBound(value, includeEqual = true) { let lowerBound = null; const lowerBoundRecursive = (current) => { - if (current === null) { - return lowerBound; - } + if (current === null) return lowerBound; - const currentKey = current.getKey(); - if (currentKey < k || (includeEqual && currentKey === k)) { - if (lowerBound === null || lowerBound.getKey() <= currentKey) { + const compare = this._compare(value, current.getValue()); + if (compare > 0 || (includeEqual && compare === 0)) { + if (lowerBound === null || this._compare(lowerBound.getValue(), current.getValue()) <= 0) { lowerBound = current; } return lowerBoundRecursive(current.getRight()); @@ -185,21 +167,19 @@ class BinarySearchTree { /** * Returns the node with the smallest key bigger or equal k * @public - * @param {number|string} k + * @param {number|string|object} value * @param {boolean} includeEqual * @return {BinarySearchTreeNode|null} */ - upperBound(k, includeEqual = true) { + upperBound(value, includeEqual = true) { let upperBound = null; const upperBoundRecursive = (current) => { - if (current === null) { - return upperBound; - } + if (current === null) return upperBound; - const currentKey = current.getKey(); - if (currentKey > k || (includeEqual && currentKey === k)) { - if (upperBound === null || upperBound.getKey() >= currentKey) { + const compare = this._compare(value, current.getValue()); + if (compare < 0 || (includeEqual && compare === 0)) { + if (upperBound === null || this._compare(upperBound.getValue(), current.getValue()) >= 0) { upperBound = current; } return upperBoundRecursive(current.getLeft()); @@ -243,30 +223,23 @@ class BinarySearchTree { /** * Removes a node by its key * @public - * @param {number|string} key + * @param {number|string|object} value * @return {boolean} */ - remove(key) { - const removeRecursively = (k, current) => { - if (current === null) { - return false; - } + remove(value) { + const removeRecursively = (val, current) => { + if (current === null) return false; - if (k < current.getKey()) { - return removeRecursively(k, current.getLeft()); - } - - if (k > current.getKey()) { - return removeRecursively(k, current.getRight()); - } + const compare = this._compare(val, current.getValue()); + if (compare < 0) return removeRecursively(val, current.getLeft()); + if (compare > 0) return removeRecursively(val, current.getRight()); // current node is the node to remove - // case 1: node has no children if (current.isLeaf()) { if (current.isRoot()) { this._root = null; - } else if (k < current.getParent().getKey()) { + } else if (this._compare(val, current.getParent().getValue()) < 0) { current.getParent().setLeft(null); } else { current.getParent().setRight(null); @@ -279,7 +252,7 @@ class BinarySearchTree { if (!current.hasRight()) { if (current.isRoot()) { this._root = current.getLeft(); - } else if (k < current.getParent().getKey()) { + } else if (this._compare(val, current.getParent().getValue()) < 0) { current.getParent().setLeft(current.getLeft()); } else { current.getParent().setRight(current.getLeft()); @@ -293,7 +266,7 @@ class BinarySearchTree { if (!current.hasLeft()) { if (current.isRoot()) { this._root = current.getRight(); - } else if (k < current.getParent().getKey()) { + } else if (this._compare(val, current.getParent().getValue()) < 0) { current.getParent().setLeft(current.getRight()); } else { current.getParent().setRight(current.getRight()); @@ -305,11 +278,11 @@ class BinarySearchTree { // case 4: node has left and right children const minRight = this.min(current.getRight()); - current.setKey(minRight.getKey()).setValue(minRight.getValue()); - return removeRecursively(minRight.getKey(), minRight); + current.setValue(minRight.getValue()); + return removeRecursively(minRight.getValue(), minRight); }; - return removeRecursively(key, this._root); + return removeRecursively(value, this._root); } /** diff --git a/src/binarySearchTreeNode.js b/src/binarySearchTreeNode.js index 8b04a71..b185b3a 100644 --- a/src/binarySearchTreeNode.js +++ b/src/binarySearchTreeNode.js @@ -8,8 +8,7 @@ * @class BinarySearchTreeNode */ class BinarySearchTreeNode { - constructor(key, value) { - this._key = key; + constructor(value) { this._value = value; this._left = null; this._right = null; @@ -18,25 +17,7 @@ class BinarySearchTreeNode { /** * @public - * @param {number|string} - * @returns {BinarySearchTreeNode} - */ - setKey(key) { - this._key = key; - return this; - } - - /** - * @public - * @return {number|string} - */ - getKey() { - return this._key; - } - - /** - * @public - * @param {any} value + * @param {number|string|object} value * @returns {BinarySearchTreeNode} */ setValue(value) { @@ -46,7 +27,7 @@ class BinarySearchTreeNode { /** * @public - * @return {any} + * @return {number|string|object} */ getValue() { return this._value; @@ -54,12 +35,12 @@ class BinarySearchTreeNode { /** * @public - * @param {BinarySearchTreeNode|null} left + * @param {BinarySearchTreeNode} left * @returns {BinarySearchTreeNode} */ setLeft(left) { if (left && !(left instanceof BinarySearchTreeNode)) { - throw new Error('setLeft expects a BinarySearchTreeNode or null'); + throw new Error('setLeft expects a BinarySearchTreeNode'); } this._left = left || null; diff --git a/test/avlTree.test.js b/test/avlTree.test.js index 125dd1e..42f7193 100644 --- a/test/avlTree.test.js +++ b/test/avlTree.test.js @@ -4,11 +4,11 @@ const { AvlTree } = require('../src/avlTree'); describe('AvlTree tests', () => { const avlTree = new AvlTree(); - describe('.insert(key, value)', () => { + describe('.insert(value)', () => { it('left rotation balancing', () => { - avlTree.insert(50, 'n1'); - avlTree.insert(80, 'n2'); - avlTree.insert(90, 'n3'); + avlTree.insert(50); + avlTree.insert(80); + avlTree.insert(90); /* 50 (balance = -2) \ @@ -23,18 +23,18 @@ describe('AvlTree tests', () => { 50 90 */ const root = avlTree.root(); - expect(root.getKey()).to.equal(80); + expect(root.getValue()).to.equal(80); - expect(root.getRight().getKey()).to.equal(90); - expect(root.getRight().getParent().getKey()).to.equal(80); + expect(root.getRight().getValue()).to.equal(90); + expect(root.getRight().getParent().getValue()).to.equal(80); - expect(root.getLeft().getKey()).to.equal(50); - expect(root.getLeft().getParent().getKey()).to.equal(80); + expect(root.getLeft().getValue()).to.equal(50); + expect(root.getLeft().getParent().getValue()).to.equal(80); }); it('right rotation balancing', () => { - avlTree.insert(40, 'n4'); - avlTree.insert(30, 'n5'); + avlTree.insert(40); + avlTree.insert(30); /* 80 @@ -54,21 +54,21 @@ describe('AvlTree tests', () => { 30 50 */ const root = avlTree.root(); - expect(root.getKey()).to.equal(80); + expect(root.getValue()).to.equal(80); - expect(root.getRight().getKey()).to.equal(90); - expect(root.getRight().getParent().getKey()).to.equal(80); + expect(root.getRight().getValue()).to.equal(90); + expect(root.getRight().getParent().getValue()).to.equal(80); - expect(root.getLeft().getKey()).to.equal(40); - expect(root.getLeft().getParent().getKey()).to.equal(80); + expect(root.getLeft().getValue()).to.equal(40); + expect(root.getLeft().getParent().getValue()).to.equal(80); - expect(root.getLeft().getRight().getKey()).to.equal(50); - expect(root.getLeft().getRight().getParent().getKey()).to.equal(40); + expect(root.getLeft().getRight().getValue()).to.equal(50); + expect(root.getLeft().getRight().getParent().getValue()).to.equal(40); - expect(root.getLeft().getLeft().getKey()).to.equal(30); - expect(root.getLeft().getLeft().getParent().getKey()).to.equal(40); + expect(root.getLeft().getLeft().getValue()).to.equal(30); + expect(root.getLeft().getLeft().getParent().getValue()).to.equal(40); - avlTree.insert(20, 'n6'); + avlTree.insert(20); /* 80 (balance = 2) / \ @@ -86,20 +86,20 @@ describe('AvlTree tests', () => { / / \ 20 50 90 */ - expect(avlTree.root().getKey()).to.equal(40); + expect(avlTree.root().getValue()).to.equal(40); - expect(avlTree.root().getLeft().getKey()).to.equal(30); - expect(avlTree.root().getLeft().getLeft().getKey()).to.equal(20); + expect(avlTree.root().getLeft().getValue()).to.equal(30); + expect(avlTree.root().getLeft().getLeft().getValue()).to.equal(20); - expect(avlTree.root().getRight().getKey()).to.equal(80); - expect(avlTree.root().getRight().getRight().getKey()).to.equal(90); - expect(avlTree.root().getRight().getLeft().getKey()).to.equal(50); + expect(avlTree.root().getRight().getValue()).to.equal(80); + expect(avlTree.root().getRight().getRight().getValue()).to.equal(90); + expect(avlTree.root().getRight().getLeft().getValue()).to.equal(50); }); it('left-right rotation balancing', () => { - avlTree.insert(35, 'n7'); - avlTree.insert(10, 'n8'); - avlTree.insert(15, 'n9'); + avlTree.insert(35); + avlTree.insert(10); + avlTree.insert(15); /* verify left-right rotation 40 @@ -122,22 +122,22 @@ describe('AvlTree tests', () => { 10 20 */ const root = avlTree.root(); - expect(root.getKey()).to.equal(40); + expect(root.getValue()).to.equal(40); - expect(root.getRight().getKey()).to.equal(80); - expect(root.getRight().getRight().getKey()).to.equal(90); - expect(root.getRight().getLeft().getKey()).to.equal(50); + expect(root.getRight().getValue()).to.equal(80); + expect(root.getRight().getRight().getValue()).to.equal(90); + expect(root.getRight().getLeft().getValue()).to.equal(50); - expect(root.getLeft().getKey()).to.equal(30); - expect(root.getLeft().getRight().getKey()).to.equal(35); - expect(root.getLeft().getLeft().getKey()).to.equal(15); - expect(root.getLeft().getLeft().getRight().getKey()).to.equal(20); - expect(root.getLeft().getLeft().getLeft().getKey()).to.equal(10); + expect(root.getLeft().getValue()).to.equal(30); + expect(root.getLeft().getRight().getValue()).to.equal(35); + expect(root.getLeft().getLeft().getValue()).to.equal(15); + expect(root.getLeft().getLeft().getRight().getValue()).to.equal(20); + expect(root.getLeft().getLeft().getLeft().getValue()).to.equal(10); }); it('right-left rotation balancing', () => { - avlTree.insert(100, 'n10'); - avlTree.insert(95, 'n11'); + avlTree.insert(100); + avlTree.insert(95); /* verify right-left rotation 40 @@ -161,52 +161,52 @@ describe('AvlTree tests', () => { 10 20 90 100 */ const root = avlTree.root(); - expect(root.getKey()).to.equal(40); - - expect(root.getRight().getKey()).to.equal(80); - expect(root.getRight().getRight().getKey()).to.equal(95); - expect(root.getRight().getRight().getRight().getKey()).to.equal(100); - expect(root.getRight().getRight().getLeft().getKey()).to.equal(90); - expect(root.getRight().getLeft().getKey()).to.equal(50); - - expect(root.getLeft().getKey()).to.equal(30); - expect(root.getLeft().getRight().getKey()).to.equal(35); - expect(root.getLeft().getLeft().getKey()).to.equal(15); - expect(root.getLeft().getLeft().getRight().getKey()).to.equal(20); - expect(root.getLeft().getLeft().getLeft().getKey()).to.equal(10); + expect(root.getValue()).to.equal(40); + + expect(root.getRight().getValue()).to.equal(80); + expect(root.getRight().getRight().getValue()).to.equal(95); + expect(root.getRight().getRight().getRight().getValue()).to.equal(100); + expect(root.getRight().getRight().getLeft().getValue()).to.equal(90); + expect(root.getRight().getLeft().getValue()).to.equal(50); + + expect(root.getLeft().getValue()).to.equal(30); + expect(root.getLeft().getRight().getValue()).to.equal(35); + expect(root.getLeft().getLeft().getValue()).to.equal(15); + expect(root.getLeft().getLeft().getRight().getValue()).to.equal(20); + expect(root.getLeft().getLeft().getLeft().getValue()).to.equal(10); }); }); describe('.min()', () => { - it('get the node with min key', () => { - expect(avlTree.min().getKey(15)); + it('get the node with min value', () => { + expect(avlTree.min().getValue(15)); }); }); describe('.max()', () => { - it('get the node with min key', () => { - expect(avlTree.max().getKey(100)); + it('get the node with min value', () => { + expect(avlTree.max().getValue(100)); }); }); describe('.root()', () => { it('should get root node', () => { - expect(avlTree.root().getKey(40)); + expect(avlTree.root().getValue(40)); }); }); - describe('.find(key)', () => { - it('find a node by its key', () => { - expect(avlTree.find(35).getKey()).to.equal(35); + describe('.find(value)', () => { + it('find a node by its value', () => { + expect(avlTree.find(35).getValue()).to.equal(35); expect(avlTree.find(1000)).to.equal(null); }); }); describe('.traverseInOrder(cb)', () => { it('traverse the tree in order', () => { - const keys = []; - avlTree.traverseInOrder((node) => keys.push(node.getKey())); - expect(keys).to.deep.equal([ + const values = []; + avlTree.traverseInOrder((node) => values.push(node.getValue())); + expect(values).to.deep.equal([ 10, 15, 20, 30, 35, 40, 50, 80, 90, 95, 100 ]); }); @@ -214,9 +214,9 @@ describe('AvlTree tests', () => { describe('.traversePreOrder(cb)', () => { it('traverse the tree in order', () => { - const keys = []; - avlTree.traversePreOrder((node) => keys.push(node.getKey())); - expect(keys).to.deep.equal([ + const values = []; + avlTree.traversePreOrder((node) => values.push(node.getValue())); + expect(values).to.deep.equal([ 40, 30, 15, 10, 20, 35, 80, 50, 95, 90, 100 ]); }); @@ -224,15 +224,15 @@ describe('AvlTree tests', () => { describe('.traversePostOrder(cb)', () => { it('traverse the tree post order', () => { - const keys = []; - avlTree.traversePostOrder((node) => keys.push(node.getKey())); - expect(keys).to.deep.equal([ + const values = []; + avlTree.traversePostOrder((node) => values.push(node.getValue())); + expect(values).to.deep.equal([ 10, 20, 15, 35, 30, 50, 90, 100, 95, 80, 40 ]); }); }); - describe('.remove(key)', () => { + describe('.remove(value)', () => { it('right rotation balancing', () => { /* 40 @@ -266,16 +266,16 @@ describe('AvlTree tests', () => { 20 90 100 */ const root = avlTree.root(); - expect(root.getKey()).to.equal(40); - expect(root.getRight().getKey()).to.equal(80); - expect(root.getRight().getRight().getKey()).to.equal(95); - expect(root.getRight().getRight().getRight().getKey()).to.equal(100); - expect(root.getRight().getRight().getLeft().getKey()).to.equal(90); - expect(root.getRight().getLeft().getKey()).to.equal(50); - expect(root.getLeft().getKey()).to.equal(15); - expect(root.getLeft().getRight().getKey()).to.equal(30); - expect(root.getLeft().getRight().getLeft().getKey()).to.equal(20); - expect(root.getLeft().getLeft().getKey()).to.equal(10); + expect(root.getValue()).to.equal(40); + expect(root.getRight().getValue()).to.equal(80); + expect(root.getRight().getRight().getValue()).to.equal(95); + expect(root.getRight().getRight().getRight().getValue()).to.equal(100); + expect(root.getRight().getRight().getLeft().getValue()).to.equal(90); + expect(root.getRight().getLeft().getValue()).to.equal(50); + expect(root.getLeft().getValue()).to.equal(15); + expect(root.getLeft().getRight().getValue()).to.equal(30); + expect(root.getLeft().getRight().getLeft().getValue()).to.equal(20); + expect(root.getLeft().getLeft().getValue()).to.equal(10); }); it('right-left rotation balancing', () => { @@ -311,15 +311,15 @@ describe('AvlTree tests', () => { 90 100 */ const root = avlTree.root(); - expect(root.getKey()).to.equal(40); - expect(root.getRight().getKey()).to.equal(80); - expect(root.getRight().getRight().getKey()).to.equal(95); - expect(root.getRight().getRight().getRight().getKey()).to.equal(100); - expect(root.getRight().getRight().getLeft().getKey()).to.equal(90); - expect(root.getRight().getLeft().getKey()).to.equal(50); - expect(root.getLeft().getKey()).to.equal(20); - expect(root.getLeft().getRight().getKey()).to.equal(30); - expect(root.getLeft().getLeft().getKey()).to.equal(15); + expect(root.getValue()).to.equal(40); + expect(root.getRight().getValue()).to.equal(80); + expect(root.getRight().getRight().getValue()).to.equal(95); + expect(root.getRight().getRight().getRight().getValue()).to.equal(100); + expect(root.getRight().getRight().getLeft().getValue()).to.equal(90); + expect(root.getRight().getLeft().getValue()).to.equal(50); + expect(root.getLeft().getValue()).to.equal(20); + expect(root.getLeft().getRight().getValue()).to.equal(30); + expect(root.getLeft().getLeft().getValue()).to.equal(15); }); it('left rotation balancing', () => { @@ -354,13 +354,13 @@ describe('AvlTree tests', () => { / \ / \ 15 30 80 100 */ - expect(avlTree.root().getKey()).to.equal(40); - expect(avlTree.root().getRight().getKey()).to.equal(95); - expect(avlTree.root().getRight().getRight().getKey()).to.equal(100); - expect(avlTree.root().getRight().getLeft().getKey()).to.equal(80); - expect(avlTree.root().getLeft().getKey()).to.equal(20); - expect(avlTree.root().getLeft().getRight().getKey()).to.equal(30); - expect(avlTree.root().getLeft().getLeft().getKey()).to.equal(15); + expect(avlTree.root().getValue()).to.equal(40); + expect(avlTree.root().getRight().getValue()).to.equal(95); + expect(avlTree.root().getRight().getRight().getValue()).to.equal(100); + expect(avlTree.root().getRight().getLeft().getValue()).to.equal(80); + expect(avlTree.root().getLeft().getValue()).to.equal(20); + expect(avlTree.root().getLeft().getRight().getValue()).to.equal(30); + expect(avlTree.root().getLeft().getLeft().getValue()).to.equal(15); }); it('left-right rotation balancing', () => { @@ -394,13 +394,13 @@ describe('AvlTree tests', () => { 15 30 80 95 */ const root = avlTree.root(); - expect(root.getKey()).to.equal(40); - expect(root.getLeft().getKey()).to.equal(20); - expect(root.getLeft().getRight().getKey()).to.equal(30); - expect(root.getLeft().getLeft().getKey()).to.equal(15); - expect(root.getRight().getKey()).to.equal(85); - expect(root.getRight().getLeft().getKey()).to.equal(80); - expect(root.getRight().getRight().getKey()).to.equal(95); + expect(root.getValue()).to.equal(40); + expect(root.getLeft().getValue()).to.equal(20); + expect(root.getLeft().getRight().getValue()).to.equal(30); + expect(root.getLeft().getLeft().getValue()).to.equal(15); + expect(root.getRight().getValue()).to.equal(85); + expect(root.getRight().getLeft().getValue()).to.equal(80); + expect(root.getRight().getRight().getValue()).to.equal(95); }); it('removes the rest of nodes properly', () => { @@ -428,22 +428,21 @@ describe('AvlTree tests', () => { 15 40 */ - expect(avlTree.root().getKey()).to.equal(20); - expect(avlTree.root().getLeft().getKey()).to.equal(15); - expect(avlTree.root().getRight().getKey()).to.equal(40); + expect(avlTree.root().getValue()).to.equal(20); + expect(avlTree.root().getLeft().getValue()).to.equal(15); + expect(avlTree.root().getRight().getValue()).to.equal(40); avlTree.remove(20); - expect(avlTree.root().getKey()).to.equal(40); - expect(avlTree.root().getValue()).to.equal('n4'); - expect(avlTree.root().getLeft().getKey()).to.equal(15); + expect(avlTree.root().getValue()).to.equal(40); + expect(avlTree.root().getLeft().getValue()).to.equal(15); avlTree.remove(40); - expect(avlTree.root().getKey()).to.equal(15); + expect(avlTree.root().getValue()).to.equal(15); expect(avlTree.count()).to.equal(1); - avlTree.insert(20, 'n12'); + avlTree.insert(20); avlTree.remove(15); - expect(avlTree.root().getKey()).to.equal(20); + expect(avlTree.root().getValue()).to.equal(20); expect(avlTree.count()).to.equal(1); avlTree.remove(20); expect(avlTree.root()).to.equal(null); diff --git a/test/binarySearchTree.test.js b/test/binarySearchTree.test.js index 83d903f..f15ed77 100644 --- a/test/binarySearchTree.test.js +++ b/test/binarySearchTree.test.js @@ -5,30 +5,27 @@ const { BinarySearchTree } = require('../src/binarySearchTree'); describe('BinarySearchTree tests', () => { const bst = new BinarySearchTree(); - describe('.insert(key, value)', () => { + describe('.insert(value)', () => { it('should insert nodes to the tree', () => { - expect(bst.insert(50, 'n1')).to.be.instanceof(BinarySearchTreeNode); - expect(bst.insert(80, 'n2')).to.be.instanceof(BinarySearchTreeNode); - expect(bst.insert(30, 'n3')).to.be.instanceof(BinarySearchTreeNode); - expect(bst.insert(90, 'n4')).to.be.instanceof(BinarySearchTreeNode); - expect(bst.insert(60, 'n5')).to.be.instanceof(BinarySearchTreeNode); - expect(bst.insert(40, 'n6')).to.be.instanceof(BinarySearchTreeNode); - expect(bst.insert(20, 'n20')).to.be.instanceof(BinarySearchTreeNode); + expect(bst.insert(50)).to.be.instanceof(BinarySearchTree); + expect(bst.insert(80)).to.be.instanceof(BinarySearchTree); + expect(bst.insert(30)).to.be.instanceof(BinarySearchTree); + expect(bst.insert(90)).to.be.instanceof(BinarySearchTree); + expect(bst.insert(60)).to.be.instanceof(BinarySearchTree); + expect(bst.insert(40)).to.be.instanceof(BinarySearchTree); + expect(bst.insert(20)).to.be.instanceof(BinarySearchTree); // updates value of existing node - expect(bst.insert(20, 'n7')).to.be.instanceof(BinarySearchTreeNode); - expect(bst.find(20).getValue()).to.equal('n7'); + expect(bst.insert(20)).to.be.instanceof(BinarySearchTree); + expect(bst.find(20).getValue()).to.equal(20); }); }); describe('.root()', () => { it('should get the root node', () => { - expect(bst.root().getKey()).to.equal(50); - expect(bst.root().getValue()).to.equal('n1'); - expect(bst.root().getRight().getKey()).to.equal(80); - expect(bst.root().getRight().getValue()).to.equal('n2'); - expect(bst.root().getLeft().getKey()).to.equal(30); - expect(bst.root().getLeft().getValue()).to.equal('n3'); + expect(bst.root().getValue()).to.equal(50); + expect(bst.root().getRight().getValue()).to.equal(80); + expect(bst.root().getLeft().getValue()).to.equal(30); }); }); @@ -38,7 +35,7 @@ describe('BinarySearchTree tests', () => { }); }); - describe('.has(key)', () => { + describe('.has(value)', () => { it('checks if a node exists by key', () => { expect(bst.has(50)).to.equal(true); expect(bst.has(80)).to.equal(true); @@ -51,7 +48,7 @@ describe('BinarySearchTree tests', () => { }); }); - describe('.find(key)', () => { + describe('.find(value)', () => { it('should search a node by its key in the tree', () => { expect(bst.find(50)).to.be.instanceof(BinarySearchTreeNode); expect(bst.find(80)).to.be.instanceof(BinarySearchTreeNode); @@ -67,23 +64,21 @@ describe('BinarySearchTree tests', () => { describe('.max()', () => { it('get the node with max key', () => { const max = bst.max(); - expect(max.getKey()).to.equal(90); - expect(max.getValue()).to.equal('n4'); + expect(max.getValue()).to.equal(90); }); }); describe('.min()', () => { it('get the node with min key', () => { const min = bst.min(); - expect(min.getKey()).to.equal(20); - expect(min.getValue()).to.equal('n7'); + expect(min.getValue()).to.equal(20); }); }); - describe('.lowerBound(k)', () => { + describe('.lowerBound(value)', () => { it('gets the node with biggest key less or equal k', () => { - expect(bst.lowerBound(60).getKey()).to.equal(60); - expect(bst.lowerBound(60, false).getKey()).to.equal(50); + expect(bst.lowerBound(60).getValue()).to.equal(60); + expect(bst.lowerBound(60, false).getValue()).to.equal(50); }); it('returns null when k is less than all tree keys', () => { @@ -96,15 +91,15 @@ describe('BinarySearchTree tests', () => { lowerBst.insert(7); lowerBst.insert(15); lowerBst.insert(9); - expect(lowerBst.floor(10).getKey()).to.equal(9); + expect(lowerBst.floor(10).getValue()).to.equal(9); }); }); describe('.upperBound(k)', () => { it('gets the node with smallest key bigger than k', () => { - expect(bst.upperBound(75).getKey()).to.equal(80); - expect(bst.upperBound(80).getKey()).to.equal(80); - expect(bst.upperBound(80, false).getKey()).to.equal(90); + expect(bst.upperBound(75).getValue()).to.equal(80); + expect(bst.upperBound(80).getValue()).to.equal(80); + expect(bst.upperBound(80, false).getValue()).to.equal(90); }); it('returns null when k is bigger than all tree keys', () => { @@ -118,14 +113,14 @@ describe('BinarySearchTree tests', () => { upperBst.insert(115062875); upperBst.insert(-38206732); upperBst.insert(49311742); - expect(upperBst.ceil(49303013).getKey()).to.equal(49311742); + expect(upperBst.ceil(49303013).getValue()).to.equal(49311742); }); }); describe('.traverseInOrder(cb)', () => { it('traverse the tree in-order', () => { const keys = []; - bst.traverseInOrder((node) => keys.push(node.getKey())); + bst.traverseInOrder((node) => keys.push(node.getValue())); expect(keys).to.deep.equal([20, 30, 40, 50, 60, 80, 90]); }); }); @@ -133,7 +128,7 @@ describe('BinarySearchTree tests', () => { describe('.traversePreOrder(cb)', () => { it('traverse the tree pre-order', () => { const keys = []; - bst.traversePreOrder((node) => keys.push(node.getKey())); + bst.traversePreOrder((node) => keys.push(node.getValue())); expect(keys).to.deep.equal([50, 30, 20, 40, 80, 60, 90]); }); }); @@ -141,12 +136,12 @@ describe('BinarySearchTree tests', () => { describe('.traversePostOrder(cb)', () => { it('traverse the tree post-order', () => { const keys = []; - bst.traversePostOrder((node) => keys.push(node.getKey())); + bst.traversePostOrder((node) => keys.push(node.getValue())); expect(keys).to.deep.equal([20, 40, 30, 60, 90, 80, 50]); }); }); - describe('.remove(key)', () => { + describe('.remove(value)', () => { it('should remove a leaf node', () => { bst.remove(20); expect(bst.has(20)).to.equal(false); @@ -157,7 +152,7 @@ describe('BinarySearchTree tests', () => { it('should remove a node with a right child only', () => { bst.remove(30); expect(bst.has(30)).to.equal(false); - expect(bst.root().getLeft().getKey()).to.equal(40); + expect(bst.root().getLeft().getValue()).to.equal(40); expect(bst.count()).to.equal(5); }); @@ -165,17 +160,16 @@ describe('BinarySearchTree tests', () => { bst.insert(30); bst.remove(40); expect(bst.has(40)).to.equal(false); - expect(bst.root().getLeft().getKey()).to.equal(30); + expect(bst.root().getLeft().getValue()).to.equal(30); expect(bst.count()).to.equal(5); }); it('should remove a node with two children', () => { bst.remove(80); expect(bst.has(80)).to.equal(false); - expect(bst.root().getRight().getKey()).to.equal(90); - expect(bst.root().getRight().getValue()).to.equal('n4'); + expect(bst.root().getRight().getValue()).to.equal(90); expect(bst.find(90).getRight()).to.equal(null); - expect(bst.find(90).getLeft().getKey()).to.equal(60); + expect(bst.find(90).getLeft().getValue()).to.equal(60); expect(bst.count()).to.equal(4); }); @@ -185,7 +179,7 @@ describe('BinarySearchTree tests', () => { bst.remove(90); bst.remove(30); bst.remove(50); - expect(bst.root().getKey()).to.equal(100); + expect(bst.root().getValue()).to.equal(100); }); it('should remove root node with left child', () => { @@ -195,7 +189,7 @@ describe('BinarySearchTree tests', () => { bst.remove(30); bst.remove(25); bst.remove(100); - expect(bst.root().getKey()).to.equal(20); + expect(bst.root().getValue()).to.equal(20); }); it('should remove root node', () => {