mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-26 15:11:16 +08:00
Add binary search tree.
This commit is contained in:
parent
1c911aadf0
commit
9f8e763d69
@ -18,6 +18,38 @@ export default class BinaryTreeNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
removeChild(nodeToRemove) {
|
||||
if (this.left && this.left === nodeToRemove) {
|
||||
this.left = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.right && this.right === nodeToRemove) {
|
||||
this.right = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
replaceChild(nodeToReplace, replacementNode) {
|
||||
if (!nodeToReplace || !replacementNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.left && this.left === nodeToReplace) {
|
||||
this.left = replacementNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.right && this.right === nodeToReplace) {
|
||||
this.right = replacementNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
traverseInOrder() {
|
||||
let traverse = [];
|
||||
|
||||
|
@ -51,4 +51,53 @@ describe('BinaryTreeNode', () => {
|
||||
|
||||
expect(rootNode.toString()).toBe('1,2,3');
|
||||
});
|
||||
|
||||
it('should remove child node', () => {
|
||||
const leftNode = new BinaryTreeNode(1);
|
||||
const rightNode = new BinaryTreeNode(3);
|
||||
const rootNode = new BinaryTreeNode(2);
|
||||
|
||||
rootNode
|
||||
.setLeft(leftNode)
|
||||
.setRight(rightNode);
|
||||
|
||||
expect(rootNode.traverseInOrder()).toEqual([1, 2, 3]);
|
||||
|
||||
expect(rootNode.removeChild(rootNode.left)).toBeTruthy();
|
||||
expect(rootNode.traverseInOrder()).toEqual([2, 3]);
|
||||
|
||||
expect(rootNode.removeChild(rootNode.right)).toBeTruthy();
|
||||
expect(rootNode.traverseInOrder()).toEqual([2]);
|
||||
|
||||
expect(rootNode.removeChild(rootNode.right)).toBeFalsy();
|
||||
expect(rootNode.traverseInOrder()).toEqual([2]);
|
||||
});
|
||||
|
||||
it('should replace child node', () => {
|
||||
const leftNode = new BinaryTreeNode(1);
|
||||
const rightNode = new BinaryTreeNode(3);
|
||||
const rootNode = new BinaryTreeNode(2);
|
||||
|
||||
rootNode
|
||||
.setLeft(leftNode)
|
||||
.setRight(rightNode);
|
||||
|
||||
expect(rootNode.traverseInOrder()).toEqual([1, 2, 3]);
|
||||
|
||||
const replacementNode = new BinaryTreeNode(5);
|
||||
rightNode.setRight(replacementNode);
|
||||
|
||||
expect(rootNode.traverseInOrder()).toEqual([1, 2, 3, 5]);
|
||||
|
||||
expect(rootNode.replaceChild(rootNode.right, rootNode.right.right)).toBeTruthy();
|
||||
expect(rootNode.right.value).toBe(5);
|
||||
expect(rootNode.right.right).toBeNull();
|
||||
expect(rootNode.traverseInOrder()).toEqual([1, 2, 5]);
|
||||
|
||||
expect(rootNode.replaceChild(rootNode.right, rootNode.right.right)).toBeFalsy();
|
||||
expect(rootNode.traverseInOrder()).toEqual([1, 2, 5]);
|
||||
|
||||
expect(rootNode.replaceChild(rootNode.right, replacementNode)).toBeTruthy();
|
||||
expect(rootNode.traverseInOrder()).toEqual([1, 2, 5]);
|
||||
});
|
||||
});
|
||||
|
@ -20,16 +20,34 @@ export default class BinarySearchTree {
|
||||
throw new Error('Item not found in the tree');
|
||||
}
|
||||
|
||||
const { parent } = nodeToRemove;
|
||||
|
||||
if (!nodeToRemove.left && !nodeToRemove.right) {
|
||||
// Node is a leaf and thus has no children.
|
||||
// Just remove the pointer to this node from the parent node.
|
||||
parent.removeChild(nodeToRemove);
|
||||
} else if (nodeToRemove.left && nodeToRemove.right) {
|
||||
// Node has two children.
|
||||
// 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) {
|
||||
this.remove(nextBiggerNode.value);
|
||||
nodeToRemove.value = nextBiggerNode.value;
|
||||
} else {
|
||||
// In case if next right value is the next bigger one and it doesn't have left child
|
||||
// then just replace node that is going to be deleted with the right node.
|
||||
nodeToRemove.value = nodeToRemove.right.value;
|
||||
nodeToRemove.right = nodeToRemove.right.right;
|
||||
}
|
||||
} else {
|
||||
// Node has only one child.
|
||||
// Make this child to be a direct child of current node's parent.
|
||||
if (nodeToRemove.left) {
|
||||
parent.replaceChild(nodeToRemove, nodeToRemove.left);
|
||||
} else {
|
||||
parent.replaceChild(nodeToRemove, nodeToRemove.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,4 +31,83 @@ describe('BinarySearchTree', () => {
|
||||
expect(bst.contains(20)).toBeTruthy();
|
||||
expect(bst.contains(40)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should remove leaf nodes', () => {
|
||||
const bst = new BinarySearchTree();
|
||||
|
||||
bst.insert(10);
|
||||
bst.insert(20);
|
||||
bst.insert(5);
|
||||
|
||||
expect(bst.toString()).toBe('5,10,20');
|
||||
|
||||
bst.remove(5);
|
||||
expect(bst.toString()).toBe('10,20');
|
||||
bst.remove(20);
|
||||
expect(bst.toString()).toBe('10');
|
||||
});
|
||||
|
||||
it('should remove nodes with one child', () => {
|
||||
const bst = new BinarySearchTree();
|
||||
|
||||
bst.insert(10);
|
||||
bst.insert(20);
|
||||
bst.insert(5);
|
||||
bst.insert(30);
|
||||
|
||||
expect(bst.toString()).toBe('5,10,20,30');
|
||||
|
||||
bst.remove(20);
|
||||
expect(bst.toString()).toBe('5,10,30');
|
||||
|
||||
bst.insert(1);
|
||||
expect(bst.toString()).toBe('1,5,10,30');
|
||||
|
||||
bst.remove(5);
|
||||
expect(bst.toString()).toBe('1,10,30');
|
||||
});
|
||||
|
||||
it('should remove nodes with two children', () => {
|
||||
const bst = new BinarySearchTree();
|
||||
|
||||
bst.insert(10);
|
||||
bst.insert(20);
|
||||
bst.insert(5);
|
||||
bst.insert(30);
|
||||
bst.insert(15);
|
||||
bst.insert(25);
|
||||
|
||||
expect(bst.toString()).toBe('5,10,15,20,25,30');
|
||||
expect(bst.root.find(20).left.value).toBe(15);
|
||||
expect(bst.root.find(20).right.value).toBe(30);
|
||||
|
||||
bst.remove(20);
|
||||
expect(bst.toString()).toBe('5,10,15,25,30');
|
||||
|
||||
bst.remove(15);
|
||||
expect(bst.toString()).toBe('5,10,25,30');
|
||||
|
||||
bst.remove(10);
|
||||
expect(bst.toString()).toBe('5,25,30');
|
||||
expect(bst.root.value).toBe(25);
|
||||
|
||||
bst.remove(25);
|
||||
expect(bst.toString()).toBe('5,30');
|
||||
|
||||
bst.remove(5);
|
||||
expect(bst.toString()).toBe('30');
|
||||
});
|
||||
|
||||
it('should throw error when trying to remove not existing node', () => {
|
||||
const bst = new BinarySearchTree();
|
||||
|
||||
bst.insert(10);
|
||||
bst.insert(20);
|
||||
|
||||
function removeNotExistingElementFromTree() {
|
||||
bst.remove(30);
|
||||
}
|
||||
|
||||
expect(removeNotExistingElementFromTree).toThrow();
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user