diff --git a/src/data-structures/tree/binary-search-tree/BinarySearchTree.js b/src/data-structures/tree/binary-search-tree/BinarySearchTree.js index baa738a4..1c2937ee 100644 --- a/src/data-structures/tree/binary-search-tree/BinarySearchTree.js +++ b/src/data-structures/tree/binary-search-tree/BinarySearchTree.js @@ -5,18 +5,31 @@ export default class BinarySearchTree { this.root = new BinarySearchTreeNode(); } + /** + * @param {*} value + */ insert(value) { this.root.insert(value); } + /** + * @param {*} value + * @return {boolean} + */ contains(value) { return this.root.contains(value); } + /** + * @param {*} value + */ remove(value) { return this.root.remove(value); } + /** + * @return {string} + */ toString() { return this.root.toString(); } diff --git a/src/data-structures/tree/binary-search-tree/BinarySearchTreeNode.js b/src/data-structures/tree/binary-search-tree/BinarySearchTreeNode.js index 07e9eab2..f71a7b5b 100644 --- a/src/data-structures/tree/binary-search-tree/BinarySearchTreeNode.js +++ b/src/data-structures/tree/binary-search-tree/BinarySearchTreeNode.js @@ -1,20 +1,37 @@ import BinaryTreeNode from '../BinaryTreeNode'; +import Comparator from '../../../utils/comparator/Comparator'; export default class BinarySearchTreeNode extends BinaryTreeNode { + /** + * @param {*} value + * @param {BinaryTreeNode} parent + * @param {function} compareFunction + */ + constructor(value = null, parent = null, compareFunction = undefined) { + super(value, parent); + + // This comparator is used to compare node values with each other. + this.nodeValueComparator = new Comparator(compareFunction); + } + + /** + * @param {*} value + * @return {BinarySearchTreeNode} + */ insert(value) { - if (this.value === null) { + if (this.nodeValueComparator.equal(this.value, null)) { this.value = value; return this; } - if (value < this.value) { + if (this.nodeValueComparator.lessThan(value, this.value)) { // Insert to the left. if (this.left) { this.left.insert(value); } else { this.setLeft(new BinarySearchTreeNode(value)); } - } else if (value > this.value) { + } else if (this.nodeValueComparator.greaterThan(value, this.value)) { // Insert to the right. if (this.right) { this.right.insert(value); @@ -26,16 +43,20 @@ export default class BinarySearchTreeNode extends BinaryTreeNode { return this; } + /** + * @param {*} value + * @return {BinarySearchTreeNode} + */ find(value) { // Check the root. - if (this.value === value) { + if (this.nodeValueComparator.equal(this.value, value)) { return this; } - if (value < this.value && this.left) { + if (this.nodeValueComparator.lessThan(value, this.value) && this.left) { // Check left nodes. return this.left.find(value); - } else if (value > this.value && this.right) { + } else if (this.nodeValueComparator.greaterThan(value, this.value) && this.right) { // Check right nodes. return this.right.find(value); } @@ -43,10 +64,17 @@ export default class BinarySearchTreeNode extends BinaryTreeNode { return null; } + /** + * @param {*} value + * @return {boolean} + */ contains(value) { return !!this.find(value); } + /** + * @param {*} value + */ remove(value) { const nodeToRemove = this.find(value); @@ -65,7 +93,7 @@ export default class BinarySearchTreeNode extends BinaryTreeNode { // Find the next biggest value (minimum value in the right branch) // and replace current value node with that next biggest value. const nextBiggerNode = nodeToRemove.right.findMin(); - if (nextBiggerNode !== nodeToRemove.right) { + if (!this.nodeComparator.equal(nextBiggerNode, nodeToRemove.right)) { this.remove(nextBiggerNode.value); nodeToRemove.value = nextBiggerNode.value; } else { @@ -85,6 +113,9 @@ export default class BinarySearchTreeNode extends BinaryTreeNode { } } + /** + * @return {BinarySearchTreeNode} + */ findMin() { if (!this.left) { return this; diff --git a/src/data-structures/tree/binary-search-tree/__test__/BinarySearchTreeNode.test.js b/src/data-structures/tree/binary-search-tree/__test__/BinarySearchTreeNode.test.js index cf386ef7..18c2f8f0 100644 --- a/src/data-structures/tree/binary-search-tree/__test__/BinarySearchTreeNode.test.js +++ b/src/data-structures/tree/binary-search-tree/__test__/BinarySearchTreeNode.test.js @@ -171,4 +171,35 @@ describe('BinarySearchTreeNode', () => { expect(removeNotExistingElementFromTree).toThrow(); }); + + it('should be possible to use objects as node values', () => { + const nodeValueComparatorCallback = (a, b) => { + const normalizedA = a || { value: null }; + const normalizedB = b || { value: null }; + + if (normalizedA.value === normalizedB.value) { + return 0; + } + + return normalizedA.value < normalizedB.value ? -1 : 1; + }; + + const obj1 = { key: 'obj1', value: 1, toString: () => 'obj1' }; + const obj2 = { key: 'obj2', value: 2, toString: () => 'obj2' }; + const obj3 = { key: 'obj3', value: 3, toString: () => 'obj3' }; + + const bstNode = new BinarySearchTreeNode(obj2, null, nodeValueComparatorCallback); + bstNode.insert(obj1); + + expect(bstNode.toString()).toBe('obj1,obj2'); + expect(bstNode.contains(obj1)).toBeTruthy(); + expect(bstNode.contains(obj3)).toBeFalsy(); + + bstNode.insert(obj3); + + expect(bstNode.toString()).toBe('obj1,obj2,obj3'); + expect(bstNode.contains(obj3)).toBeTruthy(); + + expect(bstNode.findMin().value).toEqual(obj1); + }); });