Last active
August 12, 2022 18:26
-
-
Save sebinsua/9f77fb7a6feac2db75752ae9c380d14b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class BinaryTreeNode { | |
constructor(value) { | |
this.left = null; | |
this.right = null; | |
this.values = [value]; | |
} | |
get value() { | |
const [firstValue] = this.values; | |
return firstValue; | |
} | |
} | |
class BinaryTreeBase { | |
constructor([firstValue, ...remainingValues] = []) { | |
this.root = new BinaryTreeNode(firstValue); | |
for (const value of remainingValues) { | |
this.push(value); | |
} | |
} | |
toString() { | |
const levelValues = []; | |
for (const currentLevel of this.levelsIterator()) { | |
const valuesForLevel = currentLevel.map((currentNode) => { | |
if (!currentNode) { | |
return null; | |
} | |
const value = `${currentNode.value}${ | |
currentNode.values.length > 1 ? ` (${currentNode.values.length})` : "" | |
}`; | |
return value; | |
}); | |
levelValues.push(valuesForLevel); | |
} | |
return JSON.stringify(levelValues); | |
} | |
push(value) { | |
let lastNode; | |
for (const currentNode of this.traverse(value)) { | |
if (currentNode) { | |
lastNode = currentNode; | |
} | |
} | |
const direction = this._getDirection(lastNode, value); | |
if (direction) { | |
lastNode[direction] = new BinaryTreeNode(value); | |
} else { | |
lastNode.values.push(value); | |
} | |
return; | |
} | |
*levelsIterator() { | |
let currentLevel = [this.root]; | |
while (currentLevel.length) { | |
yield currentLevel; | |
const nextLevel = currentLevel.flatMap((currentNode) => { | |
return currentNode | |
? [currentNode.left, currentNode.right] | |
: [null, null]; | |
}); | |
if (nextLevel.filter(Boolean).length > 0) { | |
currentLevel = nextLevel; | |
} else { | |
currentLevel = []; | |
} | |
} | |
} | |
// BFS: | |
*levelOrderIterator() { | |
for (const currentLevel of this.levelsIterator()) { | |
for (const currentNode of currentLevel) { | |
if (currentNode) { | |
yield currentNode; | |
} | |
} | |
} | |
} | |
// DFS | |
// See: https://en.wikipedia.org/wiki/Tree_traversal#Depth-first_search | |
*preOrderIterator() { | |
function* traverse(currentNode) { | |
yield currentNode; | |
if (currentNode.left) { | |
yield* traverse(currentNode.left); | |
} | |
if (currentNode.right) { | |
yield* traverse(currentNode.right); | |
} | |
} | |
yield* traverse(this.root); | |
} | |
*inOrderIterator() { | |
function* traverse(currentNode) { | |
if (currentNode.left) { | |
yield* traverse(currentNode.left); | |
} | |
yield currentNode; | |
if (currentNode.right) { | |
yield* traverse(currentNode.right); | |
} | |
} | |
yield* traverse(this.root); | |
} | |
*postOrderIterator() { | |
function* traverse(currentNode) { | |
if (currentNode.left) { | |
yield* traverse(currentNode.left); | |
} | |
if (currentNode.right) { | |
yield* traverse(currentNode.right); | |
} | |
yield currentNode; | |
} | |
yield* traverse(this.root); | |
} | |
*reversePreOrderIterator() { | |
function* traverse(currentNode) { | |
yield currentNode; | |
if (currentNode.right) { | |
yield* traverse(currentNode.right); | |
} | |
if (currentNode.left) { | |
yield* traverse(currentNode.left); | |
} | |
} | |
yield* traverse(this.root); | |
} | |
*reverseInOrderIterator() { | |
function* traverse(currentNode) { | |
if (currentNode.right) { | |
yield* traverse(currentNode.right); | |
} | |
yield currentNode; | |
if (currentNode.left) { | |
yield* traverse(currentNode.left); | |
} | |
} | |
yield* traverse(this.root); | |
} | |
*reversePostOrderIterator() { | |
function* traverse(currentNode) { | |
if (currentNode.right) { | |
yield* traverse(currentNode.right); | |
} | |
if (currentNode.left) { | |
yield* traverse(currentNode.left); | |
} | |
yield currentNode; | |
} | |
yield* traverse(this.root); | |
} | |
} | |
class BinaryTree extends BinaryTreeBase { | |
_getDirection(node) { | |
if (!node.left) { | |
return "left"; | |
} | |
if (!node.right) { | |
return "right"; | |
} | |
return null; | |
} | |
*traverse() { | |
let previousNode = this.root; | |
for (const level of this.levelsIterator()) { | |
for (const node of level) { | |
if (!node.left) { | |
yield node; | |
return; | |
} | |
if (!node.right) { | |
yield node; | |
return; | |
} | |
yield node; | |
} | |
} | |
yield previousNode; | |
} | |
} | |
class BinarySearchTree extends BinaryTreeBase { | |
_getDirection(node, value) { | |
if (value < node.value) { | |
return "left"; | |
} else if (value > node.value) { | |
return "right"; | |
} else { | |
return null; | |
} | |
} | |
*traverse(value) { | |
let currentNode = this.root; | |
while (currentNode) { | |
yield currentNode; | |
const direction = this._getDirection(currentNode, value); | |
currentNode = direction ? currentNode[direction] : null; | |
} | |
} | |
} | |
const bn = new BinaryTree([5, 7, 2, 11, 1, 3]); | |
// `BinaryTree`: | |
// | |
// 5 | |
// 7 2 | |
// 11 1 3 | |
// `BinarySearchTree`: | |
// | |
// 5 | |
// 2 7 | |
// 1 3 11 | |
console.log(bn); | |
console.log(bn.toString()); | |
console.log("pre-order:"); | |
for (let n of bn.preOrderIterator()) { | |
console.log(n.value); | |
} | |
console.log("in-order:"); | |
for (let n of bn.inOrderIterator()) { | |
console.log(n.value); | |
} | |
console.log("post-order:"); | |
for (let n of bn.postOrderIterator()) { | |
console.log(n.value); | |
} | |
console.log("reverse pre-order:"); | |
for (let n of bn.reversePreOrderIterator()) { | |
console.log(n.value); | |
} | |
console.log("reverse in-order:"); | |
for (let n of bn.reverseInOrderIterator()) { | |
console.log(n.value); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment