Skip to content

Instantly share code, notes, and snippets.

@sebinsua
Last active August 12, 2022 18:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebinsua/9f77fb7a6feac2db75752ae9c380d14b to your computer and use it in GitHub Desktop.
Save sebinsua/9f77fb7a6feac2db75752ae9c380d14b to your computer and use it in GitHub Desktop.
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