mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-27 15:41:16 +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();
|
this.root = new BinarySearchTreeNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} value
|
||||||
|
*/
|
||||||
insert(value) {
|
insert(value) {
|
||||||
this.root.insert(value);
|
this.root.insert(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} value
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
contains(value) {
|
contains(value) {
|
||||||
return this.root.contains(value);
|
return this.root.contains(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} value
|
||||||
|
*/
|
||||||
remove(value) {
|
remove(value) {
|
||||||
return this.root.remove(value);
|
return this.root.remove(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
toString() {
|
toString() {
|
||||||
return this.root.toString();
|
return this.root.toString();
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,37 @@
|
|||||||
import BinaryTreeNode from '../BinaryTreeNode';
|
import BinaryTreeNode from '../BinaryTreeNode';
|
||||||
|
import Comparator from '../../../utils/comparator/Comparator';
|
||||||
|
|
||||||
export default class BinarySearchTreeNode extends BinaryTreeNode {
|
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) {
|
insert(value) {
|
||||||
if (this.value === null) {
|
if (this.nodeValueComparator.equal(this.value, null)) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value < this.value) {
|
if (this.nodeValueComparator.lessThan(value, this.value)) {
|
||||||
// Insert to the left.
|
// Insert to the left.
|
||||||
if (this.left) {
|
if (this.left) {
|
||||||
this.left.insert(value);
|
this.left.insert(value);
|
||||||
} else {
|
} else {
|
||||||
this.setLeft(new BinarySearchTreeNode(value));
|
this.setLeft(new BinarySearchTreeNode(value));
|
||||||
}
|
}
|
||||||
} else if (value > this.value) {
|
} else if (this.nodeValueComparator.greaterThan(value, this.value)) {
|
||||||
// Insert to the right.
|
// Insert to the right.
|
||||||
if (this.right) {
|
if (this.right) {
|
||||||
this.right.insert(value);
|
this.right.insert(value);
|
||||||
@ -26,16 +43,20 @@ export default class BinarySearchTreeNode extends BinaryTreeNode {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} value
|
||||||
|
* @return {BinarySearchTreeNode}
|
||||||
|
*/
|
||||||
find(value) {
|
find(value) {
|
||||||
// Check the root.
|
// Check the root.
|
||||||
if (this.value === value) {
|
if (this.nodeValueComparator.equal(this.value, value)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value < this.value && this.left) {
|
if (this.nodeValueComparator.lessThan(value, this.value) && this.left) {
|
||||||
// Check left nodes.
|
// Check left nodes.
|
||||||
return this.left.find(value);
|
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.
|
// Check right nodes.
|
||||||
return this.right.find(value);
|
return this.right.find(value);
|
||||||
}
|
}
|
||||||
@ -43,10 +64,17 @@ export default class BinarySearchTreeNode extends BinaryTreeNode {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} value
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
contains(value) {
|
contains(value) {
|
||||||
return !!this.find(value);
|
return !!this.find(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} value
|
||||||
|
*/
|
||||||
remove(value) {
|
remove(value) {
|
||||||
const nodeToRemove = this.find(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)
|
// Find the next biggest value (minimum value in the right branch)
|
||||||
// and replace current value node with that next biggest value.
|
// and replace current value node with that next biggest value.
|
||||||
const nextBiggerNode = nodeToRemove.right.findMin();
|
const nextBiggerNode = nodeToRemove.right.findMin();
|
||||||
if (nextBiggerNode !== nodeToRemove.right) {
|
if (!this.nodeComparator.equal(nextBiggerNode, nodeToRemove.right)) {
|
||||||
this.remove(nextBiggerNode.value);
|
this.remove(nextBiggerNode.value);
|
||||||
nodeToRemove.value = nextBiggerNode.value;
|
nodeToRemove.value = nextBiggerNode.value;
|
||||||
} else {
|
} else {
|
||||||
@ -85,6 +113,9 @@ export default class BinarySearchTreeNode extends BinaryTreeNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {BinarySearchTreeNode}
|
||||||
|
*/
|
||||||
findMin() {
|
findMin() {
|
||||||
if (!this.left) {
|
if (!this.left) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -171,4 +171,35 @@ describe('BinarySearchTreeNode', () => {
|
|||||||
|
|
||||||
expect(removeNotExistingElementFromTree).toThrow();
|
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