mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-26 23:21:18 +08:00
Make it possible to use objects as a values for binary search tree nodes.
This commit is contained in:
parent
3ae9c40416
commit
797a6f28a3
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user