Remove operation on binary search tree with only 1 node (the root) - javascript

Started writing the removal function for an unbalanced BST structure. Manually running some tests for the first case (node has no children). Decided to run it on a tree of size 1 (just the root), and for some reason it does not seem to be reassigning the root to null the way I'm expecting it to on line 3 of this statement:
return direction ?
parent[direction] :
node = null;
Then when I run inOrderTraversal on the single node tree, which should just console.log each node, and return undefined for a null tree (what I'm expecting) it simply prints the 55 as it does before the removal.
It seems to be working for all other cases where the node to remove has no children.
Here's the fiddle: https://jsfiddle.net/uvdrmwh0/6/
And the code:
"use strict";
function Node(value, left = null, right = null) {
return {
value,
left,
right
};
}
function insert(x, root) {
let currNode = root;
while (currNode) {
if (x < currNode.value) {
if (currNode.left) {
currNode = currNode.left;
} else {
currNode.left = Node(x);
return;
}
} else if (x > currNode.value) {
if (currNode.right) {
currNode = currNode.right;
} else {
currNode.right = Node(x);
return;
}
} else if (x === currNode.value) {
throw new Error("cannot insert node with the same value as an existing node");
} else {
throw new Error("undefined behavior in insert");
}
}
throw new Error("failed to insert");
}
function remove(x, node, parent = null, direction = null) {
if (node === null) return;
if (node.value === x) {
if (!node.left && !node.right) {
return direction ?
parent[direction] = null :
node = null;
} else if (node.left && !node.right) {
//TODO
}
//TODO
}
direction = x < node.value ? "left" : "right";
remove(x, node[direction], node, direction);
}
function inOrderTraversal(node) {
if (node === null) return;
inOrderTraversal(node.left);
console.log(node.value);
inOrderTraversal(node.right);
}
function BinarySearchTree(seed) {
if (!Array.isArray(seed)) {
throw new Error("BinarySearchTree must be seeded with an array");
}
let root = Node(seed[0]);
seed.slice(1).forEach(x => {
insert(x, root);
});
return root;
}
let bst = BinarySearchTree([55]);
inOrderTraversal(bst);
console.log("---------after removal---------");
remove(55, bst);
inOrderTraversal(bst);
Update:
I've noticed things like this work:
let x = { a: 1 };
function changeProperty(obj, key, newValue) {
obj[key] = newValue;
}
changeProperty(x, "a", "hello");
console.log(x.a); //prints hello
But this doesn't:
function reassignObject(obj) {
obj = { a: "some new value" };
}
reassignObject(x);
console.log(x.a); //still prints hello
It seems you can reassign properties of an object (pointers within an object) and it will change the outside reference, but reassigning the reference to the object itself doesn't?

The following change should make it work:
console.log("---------after removal---------");
bst = remove(55, bst); //change here
The changes to node happen locally in remove function. So you should set the bst to whatever is received back from remove function.
The important thing to understand here is how does javascript pass the arguments. I hope this helps.

Related

Recursive Add method for BST using Javascript not working

Below is the implementation of a BST with an insertion function for it. currently, the code wouldn't work; It would just spit out Tree { root: null }
When i tried to debug it, it seems that it successfully adds the new Node to the correct spot, but once it returns from the function, all that data is lost and it ends up not inserting anything.
here is the code:
class Node {
constructor(value) {
this.value = value
this.left = null;
this.right = null;
}
}
class Tree {
constructor() {
this.root = null
}
insert(value) {
const insertHelper = (value, node) => {
if (node === null) {
node = new Node(value)
return null
} else if (node.value === node.value) {
console.log("Value exists.")
return null;
} else if (node.value < node.value) {
return this.insertHelper(node, node.right)
} else {
return this.insertHelper(node, node.left)
}
}
return insertHelper(value, this.root)
}
}
var tree = new Tree;
tree.insert(10)
tree.insert(5)
console.log(tree);
Several issues:
this.root is never modified. Function arguments are passed by value, so if you pass this.root as argument, and the function assigns a new value to the corresponding parameter variable node, this will not affect this.root. The solution is to let the helper function return the new value of the node that is passed as argument, so you can assign it back to the root (or other node).
At several places you compare node.value with node.value. That is a mistake. The comparison should involve value.
The recursive calls pass node as first argument, while the function expects the value as first argument.
Here is the corrected code:
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class Tree {
constructor() {
this.root = null;
}
insert(value) {
const insertHelper = (value, node) => {
if (node === null) {
node = new Node(value);
} else if (node.value === value) {
console.log("Value exists.");
} else if (node.value < value) {
node.right = insertHelper(value, node.right);
} else {
node.left = insertHelper(value, node.left);
}
return node;
}
this.root = insertHelper(value, this.root);
}
}
var tree = new Tree;
tree.insert(10);
tree.insert(5);
console.log(tree);
NB: use semi-colons explicitly. Relying on the automatic semi-colon insertion is asking for trouble. One day it will hit you.

Binary Tree code - Cannot read property 'data' of undefined

class Node {
constructor(data, left, right) {
this.data = data;
this.left = left;
this.right = right;
}
}
class BST {
constructor() {
this.root = null
}
add(data) {
const node = this.root
if (node === null) {
this.root = new Node(data)
return
} else {
const searchTree = function (node) {
if (data < node.data) {
if (node.left === null) {
node.left = new Node(data)
return
} else if (node.left !== null) {
return searchTree(node.left)
}
} else if (data > node.data) {
if (node.right === null) {
node.right = new Node(data)
return
} else if (node.right !== null) {
return searchTree(node.right)
}
} else {
return null
}
}
return searchTree(node)
}
}
levelOrder() {
const arr = [];
const queue = [];
let node = this.root;
queue.push(node);
while(queue.length) {
node = queue.shift();
arr.push(node);
if(node.left !== null) queue.push(node.left);
if(node.rigth !== null) queue.push(node.right);
}
}
}
const tree = new BST()
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
console.log(tree)
Error I keep getting
if (data < node.data) {
^
TypeError: Cannot read property 'data' of undefined
I made sure to double check my code and sometimes it works other times it doesn't....
Can anyone help with this and explain thinks I'm not understanding and what I need to look into.
The issue is that the node.left and node.right are undefined since you're not assigning them any values when creating a new node but you're checking them for strict equality with null like
node.left !== null
You just simply need to change your Node class to this :
class Node {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;
}
}
OR
Use the Logcial NOT to check for a falsy value since undefined and null both are falsy values like
if( !node.left ){
.
.
}
Hope this helps !
making my comments an answer..
The problem is that node.left or node.right are not strictly null but sometimes undefined. So if you alter your comparisons to use weak equality for null which covers both null and undefined it will work.
Ie change ===null to ==null and !==null to !=null. Else make sure you intitialise left and right to null always, but using weak equality is better.
In fact in javascript (and other languages) you can simply use if (node) and if (node.left) and so on.. since either they will null/undefined or a Node instance (and not zero). So even simpler test.

Inverting a binary tree with javascript

I'm trying to invert a binary tree in javascript, but i can't figure out why I'm not getting it to print my tree. It seems like i can create my tree in the example code in the bottom, but i cant get it to save the data.
// A node contains the value, left and right pointers
class Node {
constructor(item) {
this.data = item;
this.left = this.right = null;
}
}
class BinaryTree {
constructor() {
this.root = null;
}
invert() {
this.root = this.invert(this.root);
}
invert(node) {
if (node == null)
return node;
/* recursive calls */
let left = this.invert(node.left);
let right = this.invert(node.right);
/* swap the left and right pointers */
node.left = right;
node.right = left;
return node;
}
printTree() {
this.printTree(this.root);
}
// print InOrder binary tree traversal.
printTree(node) {
if (node == null)
return;
this.printTree(node.left);
console.log(node.data + " ");
this.printTree(node.right);
}
}
/* testing for example nodes */
const tree = new BinaryTree();
tree.root = new Node(2);
tree.root.left = new Node(11);
tree.root.right = new Node(4);
tree.root.right.left = new Node(13);
tree.root.right.right = new Node(5);
/* log inorder traversal of the input tree */
console.log("Inorder traversal of input tree is :");
tree.printTree();
console.log("");
/* invert tree */
tree.invert();
/* log inorder traversal of the minor tree */
console.log("Inorder traversal of binary tree is : ");
tree.printTree();
What am i doing wrong here to not get it to print the tree, and then invert it.
You can't overload functions in javascript, it will always call the same function regardless of which one "matches".
class Demo {
overload(a) {
console.log('hi');
}
overload() {
console.log('bye');
}
overload(a, b) {
console.log('wow');
}
}
const d = new Demo();
d.overload();
d.overload(1);
d.overload(1, 2);
Because you start with tree.printTree();, and it's actually calling:
printTree(node) {
if (node == null)
return;
this.printTree(node.left);
console.log(node.data + " ");
this.printTree(node.right);
}
AND you used == instead of ===, you are essentially calling:
printTree() {
if (true) {
return;
}
}

JavaScript; validateBinaryTree function gives value error on node

A coding challenge in which we are to write a function that determines if a binary tree is valid. The tree is simply a collection of BinaryTreeNodes that are manually linked together. The validateBinaryTree function should return false if any values on the left subtree are greater than the root value or false if any values on the right subtree are less, and true otherwise.
Here is the BinaryTreeNode class:
class BinaryTreeNode {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
insertLeft(value) {
this.left = new BinaryTreeNode(value);
return this.left;
}
insertRight(value) {
this.right = new BinaryTreeNode(value);
return this.right;
}
depth_first_print() {
console.log(this.value);
if (this.left) {
this.left.depth_first_print();
}
if (this.right) {
this.right.depth_first_print();
}
}
}
Here is the validateBinaryTree function:
const validateBinaryTree = (rootNode) => {
const rootValue = rootNode.value;
let isValid = true;
const validateLeft = (node) => {
if (node.value > rootValue) isValid = false;
if (node.left) {
validateLeft(node.left);
}
if (node.right) {
validateLeft(node.right);
}
}
const validateRight = (node) => {
if (node.value < rootValue) isValid = false;
if (node.left) {
validateRight(node.left);
}
if (node.right) {
validateRight(node.right);
}
}
validateLeft(rootNode.left);
validateRight(rootNode.right);
return isValid;
}
//Build an invalid binary tree which will look like this:
// 10
// /
// 50
const tree = new BinaryTreeNode(10);
tree.insertLeft(50);
The following function call should print false to the console:
console.log(validateBinaryTree(tree));
But instead I get the following error:
if (node.value < rootValue) isValid = false;
^
TypeError: Cannot read property 'value' of null
Your initial code fails because you try to invoke validateRight on rootNode.right, which is null. That's why it's actually better to place that check (against node === null case) inside validator itself.
Also I'd simplify this code by passing two separate functions inside - one for the left branch, another for the right - closured upon rootNode value. For example:
const validateBinaryTree = (rootNode) => {
const forLeft = val => val < rootNode.value;
const forRight = val => val > rootNode.value;
const validateBranch = (node, branchComparator) => {
return node === null ||
branchComparator(node.value) &&
validateBranch(node.left, branchComparator) &&
validateBranch(node.right, branchComparator);
}
return validateBranch(rootNode.left, forLeft) && validateBranch(rootNode.right, forRight);
}
This version also has a (slight) benefit of immediately stopping the check whenever failing node has been found (because of short-circuit nature of && operator in JS).

How to search a node in a non ordered binary tree?

I'm building a non ordered binary tree that looks like this
1
/ \
30 2
\ / \
31 4 3
/ \
34 32
.... and so on
My implementation has been created as follows
function BinarySearchTree() {
this._root = null;
}
BinarySearchTree.prototype = {
//restore constructor
constructor: BinarySearchTree,
add: function(value,whereToAdd,nodeToAdd){
//create a new item object, place data in
var node = {
value: value,
left: null,
right: null
},
//used to traverse the structure
current;
//special case: no items in the tree yet
if (this._root === null || whereToAdd === null || whereToAdd === undefined){
this._root = node;
} else {
current = nodeToAdd;
while(true){
//if the new value is less than this node's value, go left
if (whereToAdd === "no"){
//if there's no left, then the new node belongs there
if (current.left === null){
current.left = node;
break;
} else {
current = current.left;
}
//if the new value is greater than this node's value, go right
} else if (whereToAdd === "yes"){
//if there's no right, then the new node belongs there
if (current.right === null){
current.right = node;
break;
} else {
current = current.right;
}
//if the new value is equal to the current one, just ignore
} else {
break;
}
}
}
},
traverse: function(process){
//helper function
function inOrder(node){
if (node){
//traverse the left subtree
if (node.left !== null){
inOrder(node.left);
}
//call the process method on this node
process.call(this, node);
//traverse the right subtree
if (node.right !== null){
inOrder(node.right);
}
}
}
//start with the root
inOrder(this._root);
},
contains: function(value){
var found = false,
current = this._root;
//make sure there's a node to search
while(!found && current){
//if the value is less than the current node's, go left
if (value < current.value){
current = current.left;
//if the value is greater than the current node's, go right
} else if (value > current.value){
current = current.right;
//values are equal, found it!
} else {
found = true;
}
}
//only proceed if the node was found
return current;
},
size: function(){
var length = 0;
this.traverse(function(node){
length++;
});
return length;
},
toArray: function(){
var result = [];
this.traverse(function(node){
result.push(node.value);
});
return result;
},
toString: function(){
return this.toArray().toString();
},
};
function ArmTheTree(Tree){
Tree.add(1,null,null);
Tree.add(30,"no",Tree.contains(1));
Tree.add(31,"yes",Tree.contains(30));
Tree.add(32,"yes",Tree.contains(31));
Tree.add(33,"no",Tree.contains(32));
Tree.add(34,"no",Tree.contains(31));
Tree.add(35,"yes",Tree.contains(34));
Tree.add(36,"yes",Tree.contains(35));
Tree.add(2,"yes",Tree.contains(1));
Tree.add(4,"no",Tree.contains(2));
Tree.add(23,"no",Tree.contains(4));
Tree.add(24,"yes",Tree.contains(23));
Tree.add(25,"no",Tree.contains(23));
Tree.add(26,"yes",Tree.contains(25));
Tree.add(27,"no",Tree.contains(25));
Tree.add(28,"no",Tree.contains(27));
Tree.add(29,"yes",Tree.contains(27));
Tree.add(3,"yes",Tree.contains(2));
Tree.add(5,"yes",Tree.contains(3));
Tree.add(6,"no",Tree.contains(3));
Tree.add(7,"yes",Tree.contains(6));
Tree.add(8,"no",Tree.contains(6));
Tree.add(9,"yes",Tree.contains(8));
Tree.add(17,"no",Tree.contains(9));
Tree.add(19,"yes",Tree.contains(17));
Tree.add(20,"no",Tree.contains(17));
Tree.add(21,"yes",Tree.contains(20));
Tree.add(10,"yes",Tree.contains(9));
Tree.add(11,"yes",Tree.contains(10));
Tree.add(12,"no",Tree.contains(10));
Tree.add(15,"yes",Tree.contains(12));
Tree.add(13,"no",Tree.contains(12));
Tree.add(14,"yes",Tree.contains(13));
Tree.add(16,"no",Tree.contains(13));
};
Tree = new BinarySearchTree();
ArmTheTree(Tree);
});
How can I use my implementation of the transverse function to search for a node within the binary tree? My intention is to use this tree for a true or false game.
You can use a recursive approach with an auxiliary function _contains, which looks for the value in a node in the tree and its subtrees:
...,
_contains: function(node, value) {
if (node === null) return null;
if (node.value == value) return node;
// search in left subtree
var n = this._contains(node.left, value);
if (n) return n;
// search in right subtree
return this._contains(node.right, value);
},
contains: function(value) {
return this._contains(this._root, value);
},
...
The function returns null if the node isn't found.
If you make the function add return the newly added node, you could save yourself a lot of tree-walking when building the tree and you could probably do without the contains function. Instead of:
Tree.add(1, null, null);
Tree.add(30, "no", Tree.contains(1));
Tree.add(31, "yes", Tree.contains(30));
you'd get:
var n = {};
n[1] = Tree.add(1, null, null);
n[30] = Tree.add(30, "no", n[1]);
n[31] = Tree.add(31, "yes", n[30]);

Categories

Resources