Make it possible to use objects as a values for binary search tree nodes.

This commit is contained in:
Oleksii Trekhleb 2018-05-30 07:43:39 +03:00
parent 3ae9c40416
commit 797a6f28a3
3 changed files with 82 additions and 7 deletions

View File

@ -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();
}

View File

@ -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;

View File

@ -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);
});
});