From 80107e5a2ada6c4601d26911ccab60458cdb3547 Mon Sep 17 00:00:00 2001 From: ngtrhieu Date: Wed, 5 Aug 2020 19:57:25 +0800 Subject: [PATCH 1/4] refactor: exposed insertImpl and deleteImpl as protected methods --- src/data-structures/BinarySearchTree.ts | 123 +++++++++++------------- 1 file changed, 56 insertions(+), 67 deletions(-) diff --git a/src/data-structures/BinarySearchTree.ts b/src/data-structures/BinarySearchTree.ts index ee19d61..df24ac9 100644 --- a/src/data-structures/BinarySearchTree.ts +++ b/src/data-structures/BinarySearchTree.ts @@ -5,39 +5,9 @@ class BinarySearchTree extends BinaryTree { /** * Recursively insert a new value in the BST. * @param {number} value The value being inserted - * @param {BinaryTreeNode} node The current node. Param is not required. - * @return {void} */ insert(val: number): void { - if (!this.root) { - this.root = new BinaryTreeNode(val); - return; - } - - function insertImpl(value: number, node: BinaryTreeNode) { - const nodeValue = node.value; - - if (value < nodeValue) { - const { left } = node; - if (!left) { - node.left = new BinaryTreeNode(value); - return; - } - - insertImpl(value, left); - return; - } - - const { right } = node; - if (!right) { - node.right = new BinaryTreeNode(value); - return; - } - - insertImpl(value, right); - } - - insertImpl(val, this.root); + this.root = this._insertImpl(val, this.root); } /** @@ -71,6 +41,25 @@ class BinarySearchTree extends BinaryTree { return searchImpl(val, this.root); } + protected _insertImpl( + value: number, + node: BinaryTreeNode | null, + ): BinaryTreeNode { + if (!node) { + return new BinaryTreeNode(value); + } + + // Normal BST insert + // NOTE: Duplicates are sent to the left + if (value <= node.value) { + node.left = this._insertImpl(value, node.left); + } else { + node.right = this._insertImpl(value, node.right); + } + + return node; + } + private _getMinimumNode( node: BinaryTreeNode | null, ): BinaryTreeNode | null { @@ -126,49 +115,49 @@ class BinarySearchTree extends BinaryTree { * @return {BinaryTreeNode} The root node after deletion. */ delete(val: number): BinaryTreeNode | null { - const deleteImpl = ( - value: number, - node: BinaryTreeNode | null, - ): BinaryTreeNode | null => { - if (!node) { - return null; - } + this.root = this._deleteImpl(val, this.root); + return this.root; + } - const nodeValue = node.value; - const { left } = node; - const { right } = node; - if (value < nodeValue) { - node.left = deleteImpl(value, left); - return node; - } else if (value > nodeValue) { - node.right = deleteImpl(value, right); - return node; - } + protected _deleteImpl = ( + value: number, + node: BinaryTreeNode | null, + ): BinaryTreeNode | null => { + if (!node) { + return null; + } - if (!left && !right) { - return null; - } + const nodeValue = node.value; + const { left } = node; + const { right } = node; + if (value < nodeValue) { + node.left = this._deleteImpl(value, left); + return node; + } else if (value > nodeValue) { + node.right = this._deleteImpl(value, right); + return node; + } - if (!left) { - return right; - } + if (!left && !right) { + return null; + } - if (!right) { - return left; - } + if (!left) { + return right; + } - const tempNode: BinaryTreeNode = this._getMinimumNode( - right, - ) as BinaryTreeNode; - node.value = tempNode.value; - node.right = deleteImpl(tempNode.value, right); + if (!right) { + return left; + } - return node; - }; + const tempNode: BinaryTreeNode = this._getMinimumNode( + right, + ) as BinaryTreeNode; + node.value = tempNode.value; + node.right = this._deleteImpl(tempNode.value, right); - this.root = deleteImpl(val, this.root); - return this.root; - } + return node; + }; } export default BinarySearchTree; From 1ee7d9ce49ed76360dba35c086d8db34ef295fe4 Mon Sep 17 00:00:00 2001 From: ngtrhieu Date: Wed, 5 Aug 2020 20:02:43 +0800 Subject: [PATCH 2/4] chore: setup launch.jsonuseful for vscode Jest debugging. Just place breakpoints on your code and Ctrl+F5 away to debug :)Hints: 'test.skip' and 'test.only' are useful to run only selected tests --- .vscode/launch.json | 12 ++ src/data-structures/AvlTree.ts | 77 +++++++++++ src/index.ts | 2 + test/data-structures/AvlTree.test.ts | 200 +++++++++++++++++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 src/data-structures/AvlTree.ts create mode 100644 test/data-structures/AvlTree.test.ts diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..05b6a9e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,12 @@ +{ + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest Tests", + "program": "${workspaceRoot}/node_modules/tsdx/dist/index.js", + "args": ["test", "--runInBand"], + "internalConsoleOptions": "openOnSessionStart" + } + ] +} diff --git a/src/data-structures/AvlTree.ts b/src/data-structures/AvlTree.ts new file mode 100644 index 0000000..4083e5d --- /dev/null +++ b/src/data-structures/AvlTree.ts @@ -0,0 +1,77 @@ +import BinaryTreeNode from './BinaryTreeNode'; +import BinarySearchTree from './BinarySearchTree'; + +/** + * An implementation of AvlTree based on BinarySearchTrees + */ +class AvlTree extends BinarySearchTree { + protected _insertImpl( + value: number, + node: BinaryTreeNode | null, + ): BinaryTreeNode { + return this._balance(super._insertImpl(value, node)); + } + + protected _deleteImpl = ( + value: number, + node: BinaryTreeNode | null, + ): BinaryTreeNode | null => { + node = super._insertImpl(value, node); + return node ? this._balance(node!) : node; + }; + + _balance(node: BinaryTreeNode): BinaryTreeNode { + // Helper function to calculate the balance of the node + const calcBalance = (node: BinaryTreeNode): number => + (node.left?.height() || 0) - (node.right?.height() || 0) + 1; + + // Check for whether the node is out-of-balance + const balance = calcBalance(node); + + if (balance > 1) { + const leftBalance = calcBalance(node.left!); + if (leftBalance < 0) { + // LL case + node = this._rotateRight(node); + } else { + // LR case + node.left = this._rotateLeft(node.left!); + node = this._rotateRight(node); + } + } else if (balance < -1) { + const rightBalance = calcBalance(node.right!); + if (rightBalance < 0) { + // RL case + node.right = this._rotateRight(node.right!); + node = this._rotateLeft(node); + } else { + // RR case + node = this._rotateLeft(node); + } + } + + return node; + } + + _rotateRight(node: BinaryTreeNode): BinaryTreeNode { + const { left } = node; + const { right } = left!; + + left!.right = node; + node.left = right; + + return left!; + } + + _rotateLeft(node: BinaryTreeNode): BinaryTreeNode { + const { right } = node; + const { left } = right!; + + right!.left = node; + node.right = left; + + return right!; + } +} + +export default AvlTree; diff --git a/src/index.ts b/src/index.ts index 5ce515a..a39a75f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import topologicalSort from './algorithms/topologicalSort'; // Data Structures import BinarySearchTree from './data-structures/BinarySearchTree'; +import AvlTree from './data-structures/AvlTree'; import BinaryTree from './data-structures/BinaryTree'; import BinaryTreeNode from './data-structures/BinaryTreeNode'; import BloomFilter from './data-structures/BloomFilter'; @@ -51,6 +52,7 @@ export { topologicalSort, // Data Structures BinarySearchTree, + AvlTree, BinaryTree, BinaryTreeNode, BloomFilter, diff --git a/test/data-structures/AvlTree.test.ts b/test/data-structures/AvlTree.test.ts new file mode 100644 index 0000000..4b9d03b --- /dev/null +++ b/test/data-structures/AvlTree.test.ts @@ -0,0 +1,200 @@ +import { AvlTree } from '../../src'; +import nullthrows from '../../src/utils/nullthrows'; + +describe('AvtTree', () => { + describe('insert()', () => { + test('if empty tree, value becomes root', () => { + const tree = new AvlTree(); + tree.insert(5); + expect(nullthrows(tree.root).value).toBe(5); + }); + + test('inserts value at correct location in BST', () => { + const tree = new AvlTree(30); + tree.insert(20); + tree.insert(40); + tree.insert(35); + + const root = nullthrows(tree.root); + expect(nullthrows(root.left).value).toBe(20); + expect(nullthrows(root.right).value).toBe(40); + }); + }); + + describe('search()', () => { + test('returns correct result based on value', () => { + const tree = new AvlTree(30); + tree.insert(20); + tree.insert(40); + tree.insert(25); + tree.insert(50); + tree.insert(35); + + let searchResult = tree.search(35); + expect(searchResult).toBe(true); + searchResult = tree.search(555); + expect(searchResult).toBe(false); + }); + }); + + describe('getMinimum()', () => { + test('empty tree', () => { + const tree = new AvlTree(); + expect(tree.getMinimum()).toBe(null); + }); + + test.only('non-empty tree', () => { + const tree = new AvlTree(10); + tree.insert(5); + tree.insert(15); + tree.insert(2); + expect(tree.getMinimum()).toBe(2); + }); + }); + + describe('getMaximum()', () => { + test('empty tree', () => { + const tree = new AvlTree(); + expect(tree.getMaximum()).toBe(null); + }); + + test('non-empty tree', () => { + const tree = new AvlTree(10); + tree.insert(5); + tree.insert(15); + tree.insert(2); + expect(tree.getMaximum()).toBe(15); + }); + }); + + describe('delete()', () => { + test('delete node from single-node BST', () => { + const tree = new AvlTree(20); + tree.delete(20); + expect(tree.inOrder()).toEqual([]); + }); + + test('delete root node from BST', () => { + const tree = new AvlTree(30); + tree.insert(20); + tree.insert(40); + tree.insert(10); + tree.insert(50); + tree.insert(5); + tree.insert(6); + tree.delete(30); + expect(tree.inOrder()).toEqual([5, 6, 10, 20, 40, 50]); + }); + + test('delete value from BST which does not exists', () => { + const tree = new AvlTree(20); + tree.insert(10); + tree.insert(50); + tree.insert(60); + tree.insert(40); + tree.insert(70); + tree.insert(5); + tree.insert(6); + tree.delete(100); + expect(tree.inOrder()).toEqual([5, 6, 10, 20, 40, 50, 60, 70]); + }); + + test('deletes value from the BST', () => { + const tree = new AvlTree(50); + tree.insert(20); + tree.insert(10); + tree.insert(5); + tree.insert(15); + tree.insert(12); + tree.insert(14); + tree.insert(30); + tree.insert(35); + tree.insert(40); + tree.insert(60); + tree.insert(55); + tree.insert(80); + tree.insert(70); + tree.insert(100); + tree.delete(100); + expect(tree.inOrder()).toEqual([ + 5, + 10, + 12, + 14, + 15, + 20, + 30, + 35, + 40, + 50, + 55, + 60, + 70, + 80, + ]); + + tree.delete(10); + expect(tree.inOrder()).toEqual([ + 5, + 12, + 14, + 15, + 20, + 30, + 35, + 40, + 50, + 55, + 60, + 70, + 80, + ]); + + tree.delete(80); + expect(tree.inOrder()).toEqual([ + 5, + 12, + 14, + 15, + 20, + 30, + 35, + 40, + 50, + 55, + 60, + 70, + ]); + + tree.delete(20); + expect(tree.inOrder()).toEqual([ + 5, + 12, + 14, + 15, + 30, + 35, + 40, + 50, + 55, + 60, + 70, + ]); + + tree.delete(15); + tree.delete(60); + expect(tree.inOrder()).toEqual([5, 12, 14, 30, 35, 40, 50, 55, 70]); + + tree.delete(35); + tree.delete(12); + expect(tree.inOrder()).toEqual([5, 14, 30, 40, 50, 55, 70]); + + tree.delete(30); + expect(tree.inOrder()).toEqual([5, 14, 40, 50, 55, 70]); + + tree.delete(40); + tree.delete(50); + expect(tree.inOrder()).toEqual([5, 14, 55, 70]); + }); + }); +}); From 256d9dae898a9f0c73d21fffa764c1fdff6aa05d Mon Sep 17 00:00:00 2001 From: ngtrhieu Date: Wed, 5 Aug 2020 21:47:21 +0800 Subject: [PATCH 3/4] feat: add AvtTree the AvtTree is inherited from BinarySearchTree. However duplicates are not properly handled yet in Avl. --- src/data-structures/AvlTree.ts | 13 +- src/data-structures/BinarySearchTree.ts | 6 +- test/data-structures/AvlTree.test.ts | 301 +++++++----------- test/data-structures/BinarySearchTree.test.ts | 11 +- 4 files changed, 139 insertions(+), 192 deletions(-) diff --git a/src/data-structures/AvlTree.ts b/src/data-structures/AvlTree.ts index 4083e5d..ebe27a3 100644 --- a/src/data-structures/AvlTree.ts +++ b/src/data-structures/AvlTree.ts @@ -16,35 +16,36 @@ class AvlTree extends BinarySearchTree { value: number, node: BinaryTreeNode | null, ): BinaryTreeNode | null => { - node = super._insertImpl(value, node); + node = super._deleteImpl(value, node); return node ? this._balance(node!) : node; }; _balance(node: BinaryTreeNode): BinaryTreeNode { // Helper function to calculate the balance of the node const calcBalance = (node: BinaryTreeNode): number => - (node.left?.height() || 0) - (node.right?.height() || 0) + 1; + (node.left ? node.left!.height() + 1 : 0) - + (node.right ? node.right?.height() + 1 : 0); // Check for whether the node is out-of-balance const balance = calcBalance(node); if (balance > 1) { const leftBalance = calcBalance(node.left!); - if (leftBalance < 0) { + if (leftBalance > 0) { // LL case node = this._rotateRight(node); - } else { + } else if (leftBalance < 0) { // LR case node.left = this._rotateLeft(node.left!); node = this._rotateRight(node); } } else if (balance < -1) { const rightBalance = calcBalance(node.right!); - if (rightBalance < 0) { + if (rightBalance > 0) { // RL case node.right = this._rotateRight(node.right!); node = this._rotateLeft(node); - } else { + } else if (rightBalance < 0) { // RR case node = this._rotateLeft(node); } diff --git a/src/data-structures/BinarySearchTree.ts b/src/data-structures/BinarySearchTree.ts index df24ac9..7772fa5 100644 --- a/src/data-structures/BinarySearchTree.ts +++ b/src/data-structures/BinarySearchTree.ts @@ -119,10 +119,10 @@ class BinarySearchTree extends BinaryTree { return this.root; } - protected _deleteImpl = ( + protected _deleteImpl( value: number, node: BinaryTreeNode | null, - ): BinaryTreeNode | null => { + ): BinaryTreeNode | null { if (!node) { return null; } @@ -157,7 +157,7 @@ class BinarySearchTree extends BinaryTree { node.right = this._deleteImpl(tempNode.value, right); return node; - }; + } } export default BinarySearchTree; diff --git a/test/data-structures/AvlTree.test.ts b/test/data-structures/AvlTree.test.ts index 4b9d03b..58b2e20 100644 --- a/test/data-structures/AvlTree.test.ts +++ b/test/data-structures/AvlTree.test.ts @@ -1,200 +1,137 @@ -import { AvlTree } from '../../src'; -import nullthrows from '../../src/utils/nullthrows'; +import _ from 'lodash'; +import { AvlTree, BinaryTreeNode } from '../../src'; -describe('AvtTree', () => { - describe('insert()', () => { - test('if empty tree, value becomes root', () => { - const tree = new AvlTree(); - tree.insert(5); - expect(nullthrows(tree.root).value).toBe(5); - }); +import { binarySearchTreeTests } from './BinarySearchTree.test'; - test('inserts value at correct location in BST', () => { - const tree = new AvlTree(30); - tree.insert(20); - tree.insert(40); - tree.insert(35); - - const root = nullthrows(tree.root); - expect(nullthrows(root.left).value).toBe(20); - expect(nullthrows(root.right).value).toBe(40); - }); +describe('AvtTree', () => { + describe('normal binary test', () => { + // Test against normal BST test cases + binarySearchTreeTests(describe, test); }); - describe('search()', () => { - test('returns correct result based on value', () => { - const tree = new AvlTree(30); - tree.insert(20); - tree.insert(40); - tree.insert(25); - tree.insert(50); - tree.insert(35); - - let searchResult = tree.search(35); - expect(searchResult).toBe(true); - searchResult = tree.search(555); - expect(searchResult).toBe(false); + describe('self-balancing', () => { + describe('insert', () => { + test('LL rotation', () => { + const tree = new AvlTree(); + tree.insert(10); + tree.insert(9); + tree.insert(8); + expect(tree.height()).toBe(1); + + tree.insert(11); + expect(tree.height()).toBe(2); + }); + + test('LR rotation', () => { + const tree = new AvlTree(); + tree.insert(10); + tree.insert(8); + tree.insert(9); + expect(tree.height()).toBe(1); + + tree.insert(11); + expect(tree.height()).toBe(2); + }); + + test('RR rotation', () => { + const tree = new AvlTree(); + tree.insert(8); + tree.insert(9); + tree.insert(10); + expect(tree.height()).toBe(1); + + tree.insert(11); + expect(tree.height()).toBe(2); + }); + + test('RL rotation', () => { + const tree = new AvlTree(); + tree.insert(8); + tree.insert(10); + tree.insert(9); + expect(tree.height()).toBe(1); + + tree.insert(11); + expect(tree.height()).toBe(2); + }); }); - }); - describe('getMinimum()', () => { - test('empty tree', () => { - const tree = new AvlTree(); - expect(tree.getMinimum()).toBe(null); + describe('delete', () => { + test('LL rotation', () => { + const tree = new AvlTree(); + tree.insert(5); + tree.insert(3); + tree.insert(8); + tree.insert(4); + tree.insert(2); + tree.insert(10); + tree.insert(1); + expect(tree.height()).toBe(3); + + tree.delete(10); + expect(tree.height()).toBe(2); + }); + + test('LR rotation', () => { + const tree = new AvlTree(); + tree.insert(100); + tree.insert(200); + tree.insert(10); + tree.insert(1); + tree.insert(50); + tree.insert(1000); + tree.insert(40); + tree.insert(60); + expect(tree.height()).toBe(3); + + tree.delete(1000); + expect(tree.height()).toBe(2); + }); }); - test.only('non-empty tree', () => { - const tree = new AvlTree(10); - tree.insert(5); - tree.insert(15); - tree.insert(2); - expect(tree.getMinimum()).toBe(2); + describe('duplicate', () => { + test.skip('test for duplicates in AVL', () => { + expect(true).toBe(true); + }); }); - }); - describe('getMaximum()', () => { - test('empty tree', () => { - const tree = new AvlTree(); - expect(tree.getMaximum()).toBe(null); - }); + test.skip('random stress test', () => { + const assertAvl = (node: BinaryTreeNode | null) => { + if (!node) return; - test('non-empty tree', () => { - const tree = new AvlTree(10); - tree.insert(5); - tree.insert(15); - tree.insert(2); - expect(tree.getMaximum()).toBe(15); - }); - }); + // assert BST property + if (node.left) + expect(node.value).toBeGreaterThanOrEqual(node.left.value); + if (node.right) expect(node.value).toBeLessThan(node.right.value); - describe('delete()', () => { - test('delete node from single-node BST', () => { - const tree = new AvlTree(20); - tree.delete(20); - expect(tree.inOrder()).toEqual([]); - }); + // assert balancing property + const balance = + (node.left ? node.left!.height() + 1 : 0) - + (node.right ? node.right?.height() + 1 : 0); + expect(Math.abs(balance)).toBeLessThanOrEqual(1); - test('delete root node from BST', () => { - const tree = new AvlTree(30); - tree.insert(20); - tree.insert(40); - tree.insert(10); - tree.insert(50); - tree.insert(5); - tree.insert(6); - tree.delete(30); - expect(tree.inOrder()).toEqual([5, 6, 10, 20, 40, 50]); - }); + assertAvl(node.left); + assertAvl(node.right); + }; - test('delete value from BST which does not exists', () => { - const tree = new AvlTree(20); - tree.insert(10); - tree.insert(50); - tree.insert(60); - tree.insert(40); - tree.insert(70); - tree.insert(5); - tree.insert(6); - tree.delete(100); - expect(tree.inOrder()).toEqual([5, 6, 10, 20, 40, 50, 60, 70]); - }); + const tree = new AvlTree(); + + // Build a tree of 100 random numbers, assert the tree is AVL tree + _.times(100, () => { + tree.insert(Math.floor(Math.random() * 50)); + + assertAvl(tree.root); + }); + + // Delete off 50 random numbers, assert the tree is AVL the whole time + const arr = tree.inOrder(); + _.times(50, () => { + const index = Math.floor(Math.random() * arr.length); + tree.delete(arr[index]); + arr.splice(index, 1); - test('deletes value from the BST', () => { - const tree = new AvlTree(50); - tree.insert(20); - tree.insert(10); - tree.insert(5); - tree.insert(15); - tree.insert(12); - tree.insert(14); - tree.insert(30); - tree.insert(35); - tree.insert(40); - tree.insert(60); - tree.insert(55); - tree.insert(80); - tree.insert(70); - tree.insert(100); - tree.delete(100); - expect(tree.inOrder()).toEqual([ - 5, - 10, - 12, - 14, - 15, - 20, - 30, - 35, - 40, - 50, - 55, - 60, - 70, - 80, - ]); - - tree.delete(10); - expect(tree.inOrder()).toEqual([ - 5, - 12, - 14, - 15, - 20, - 30, - 35, - 40, - 50, - 55, - 60, - 70, - 80, - ]); - - tree.delete(80); - expect(tree.inOrder()).toEqual([ - 5, - 12, - 14, - 15, - 20, - 30, - 35, - 40, - 50, - 55, - 60, - 70, - ]); - - tree.delete(20); - expect(tree.inOrder()).toEqual([ - 5, - 12, - 14, - 15, - 30, - 35, - 40, - 50, - 55, - 60, - 70, - ]); - - tree.delete(15); - tree.delete(60); - expect(tree.inOrder()).toEqual([5, 12, 14, 30, 35, 40, 50, 55, 70]); - - tree.delete(35); - tree.delete(12); - expect(tree.inOrder()).toEqual([5, 14, 30, 40, 50, 55, 70]); - - tree.delete(30); - expect(tree.inOrder()).toEqual([5, 14, 40, 50, 55, 70]); - - tree.delete(40); - tree.delete(50); - expect(tree.inOrder()).toEqual([5, 14, 55, 70]); + assertAvl(tree.root); + }); }); }); }); diff --git a/test/data-structures/BinarySearchTree.test.ts b/test/data-structures/BinarySearchTree.test.ts index cd31a3b..b60c388 100644 --- a/test/data-structures/BinarySearchTree.test.ts +++ b/test/data-structures/BinarySearchTree.test.ts @@ -1,7 +1,12 @@ import { BinarySearchTree } from '../../src'; import nullthrows from '../../src/utils/nullthrows'; -describe('BinarySearchTree', () => { +export const binarySearchTreeTests = ( + /* eslint-disable @typescript-eslint/no-explicit-any */ + describe: any, + test: any, + /* eslint-enable @typescript-eslint/no-explicit-any */ +) => { describe('insert()', () => { test('if empty tree, value becomes root', () => { const tree = new BinarySearchTree(); @@ -197,4 +202,8 @@ describe('BinarySearchTree', () => { expect(tree.inOrder()).toEqual([5, 14, 55, 70]); }); }); +}; + +describe('BinarySearchTree', () => { + binarySearchTreeTests(describe, test); }); From 0432ee16f84f5f4128e3f0cb13a93de6a094ca7d Mon Sep 17 00:00:00 2001 From: Yangshun Tay Date: Fri, 7 Aug 2020 19:25:54 +0800 Subject: [PATCH 4/4] Update index.ts --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index a39a75f..4bcaf37 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,8 +13,8 @@ import quickSort from './algorithms/quickSort'; import topologicalSort from './algorithms/topologicalSort'; // Data Structures -import BinarySearchTree from './data-structures/BinarySearchTree'; import AvlTree from './data-structures/AvlTree'; +import BinarySearchTree from './data-structures/BinarySearchTree'; import BinaryTree from './data-structures/BinaryTree'; import BinaryTreeNode from './data-structures/BinaryTreeNode'; import BloomFilter from './data-structures/BloomFilter'; @@ -51,8 +51,8 @@ export { quickSort, topologicalSort, // Data Structures - BinarySearchTree, AvlTree, + BinarySearchTree, BinaryTree, BinaryTreeNode, BloomFilter,