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).
Related
This is my code:
form.listPrice.map(list => {
if (list.id === listId) {
form.active = true
listPrice = parseInt(list.price)
if (list.offerPrice) {
listofferPrice = parseInt(list.offerPrice)
} else {
listofferPrice = null
}
}
})
And here:
n.listPrice.map(list => {
if (list.id === listPrice) {
valid = true;
n.active = true;
n.showPrice.price = list.price;
n.showPrice.offerPrice = list.offerPrice;
n.ladder = list.ladder;
}
And this output the same warning:
Expected to return a value in arrow function array-callback-return
You are using .map incorrectly. .map should be used only when you want to construct a new array from an old array, but your code is not doing that - you're only carrying out side-effects - the setting of form.active and listofferPrice.
The first step would be to use forEach or for..of instead, eg:
for (const list of form.listPrice) {
if (list.id === listId) {
form.active = true
listPrice = parseInt(list.price)
if (list.offerPrice) {
listofferPrice = parseInt(list.offerPrice)
} else {
listofferPrice = null
}
}
}
But since it looks like you're trying to find a possible single matching value in the array, .find would be more appropriate:
const found = form.listPrice.find(list => list.id === listId);
if (found) {
form.active = true
listPrice = parseInt(found.price)
if (found.offerPrice) {
listofferPrice = parseInt(found.offerPrice)
} else {
listofferPrice = null
}
}
const found = n.listPrice.find(list => list.id === listPrice);
if (found) {
valid = true;
n.active = true;
n.showPrice.price = found.price;
n.showPrice.offerPrice = found.offerPrice;
n.ladder = found.ladder;
}
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.
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.
I am creating a react native application.
I have a back button that fires the function findItem. findItem the uses async method searchJson. searchJson searches recursive json to find parent object based on id. However it never returns any results.
findItem:
findItem() {
//Pass null so top level json will be pulled
let result = this.searchJson(null).done();
let abv = 2;
// this.setState(previousState => {
// return {
// data: result,
// parentID: result.parentid
// };
// });
}
searchJson:
async searchJson(object) {
return new Promise(resolve => {
//use object or pull from porp - all data
let theObject = object == null ? this.props.data : object;
var result = null;
if (theObject instanceof Array) {
for (var i = 0; i < theObject.length; i++) {
result = this.searchJson(theObject[i]);
if (result) {
break;
}
}
}
else {
for (var prop in theObject) {
console.log(prop + ': ' + theObject[prop]);
if (prop == 'id') {
if (theObject[prop] == this.state.parentID) {
return theObject;
}
}
if (theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
result = this.searchJson(theObject[prop]);
if (result) {
break;
}
}
}
}
if(result != null)
resolve(result);
});
}
Any help will be greatly appreciated.
Ok so I never got this to work but my workaround was this.
I Modified the findItem method:
findItem() {
let FinNode = null;
for (var node in this.props.data) {
FinNode = this.searchJson(this.state.parentID, this.props.data, this.props.data[node].book);
if (FinNode != null) {
this.setState(previousState => {
return {
data: FinNode[0].book.parentid == "" ? null : FinNode,
parentID: FinNode[0].book.parentid
};
});
break;
}
}
}
And then the searchJson:
searchJson(id, parentArray, currentNode) {
if (id == currentNode.id) {
return parentArray;
} else {
var result;
for (var index in currentNode.books) {
var node = currentNode.books[index].book;
if (node.id == id)
return currentNode.books;
this.searchJson(id, currentNode.books, node);
}
return null;
}
}
This allowed for all my nodes to be searched and the for loop made so that there is no need for async. This does have some drawbacks but seems to work decently without any massive performance issues.
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.