Explanation of class Definition for Binary Trees in leetcode - javascript

Was hoping someone could help me understand how this class works.
I'm currently taking a javascript algorithms in udemy and the way they explain how to do all operations in a binary tree is a little different than what leetcode shows.
In the course, the tree definition is the same or very similar to leetcode:
class Node {
constructor(value){
this.value = value;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
constructor(){
this.root = null;
}
}
however, the values are first inserted as nodes before doing any other operation:
insert(value){
var newNode = new Node(value);
if(this.root === null){
this.root = newNode;
return this;
}
var current = this.root;
while(true){
if(value === current.value) return undefined;
if(value < current.value){
if(current.left === null){
current.left = newNode;
return this;
}
current = current.left;
} else {
if(current.right === null){
current.right = newNode;
return this;
}
current = current.right;
}
}
}
On Leetcode, the values are passed as an array, and thats what throws me off a little:
Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* #param {TreeNode} root
* #return {number}
Looking at a simple solution for finding the max depth:
var maxDepth = function(root) {
if(!root) return 0;
return Math.max(maxDepth(root.left) , maxDepth(root.right) ) +1
};
given the array root = [3,9,20,null,null,15,7],
how do we know that root.left is 9 and root.right is 20. Then the next level, root.left.left is null and root.left.right is null. Then root.right.left is 15 and root.right.right is 7.
Just not sure how the array translates into that
Thanks!
tried adding the nodes one by one then perfomring binary tree operations

On Leetcode, the values are passed as an array
This is a misunderstanding.
Values are not passed as an array. Your function gets an instance of TreeNode as argument (or null). LeetCode let's you specify input in a kind of JSON format, but that is just text that LeetCode will first translate to a TreeNode based tree, before calling your function.
The text input follows a kind of bread-first traversal of the tree it is supposed to represent. So [3,9,20,null,null,15,7] represents this tree:
3
/ \
9 20
/ \
15 7
The two null occurrences indicate that the node with value 9 has no left nor right child.
In case you want to convert an array (based on the input notation) to a tree yourself (the task that LeetCode does for you), you could use a function like this:
function TreeNode(val, left, right) { // LeetCode's code
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
function makeTree(arr) {
if (!arr) return null; // empty tree
const values = arr[Symbol.iterator]();
const root = new TreeNode(values.next().value);
const queue = new Set().add(root);
for (const node of queue) {
for (const side of ["left", "right"]) {
const value = values.next().value;
if (value != null) queue.add(node[side] = new TreeNode(value));
}
}
return root;
}
// Example use
const arr = [3,9,20,null,null,15,7];
const root = makeTree(arr);
console.log(root);

That array representation is usually called a "heap". The positions in the array represent nodes in the tree based on the index. A node at position n has children at positions 2n+1 and 2n+2. The node at position zero is the root.
The root's children are at 2x0+1 and 2x0+2, positions 1 and 2 in the array. The left child of the root at position 1 is 9, and it's children are at positions 3 and 4, the two null values.
edit — apparently the LeetCode representation isn't always a heap, but heaps are a useful tree representation nevertheless so I'll leave this be.

Related

How to get sum of children and check if it's equal to root?

I was solving a problem at leetcode and it was to get if the root equals the sum of children
this is my code
var checkTree = function (root) {
root[0] = root[0] === undefined ? 0 : root[0];
root[1] = root[1] === undefined ? null : root[1];
root[2] = root[2] === undefined ? null : root[2];
if (root[0] === root[1] + root[2]) {
return true;
} else {
return false;
}
};
checkTree([10, 4, 6]);
but when i submit it says it is wrong.
here's the link to the problem: https://leetcode.com/problems/root-equals-sum-of-children/
EXPLANATION
So the problem gives you the definition of the node like this
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
Inside checkTree function you receive root as a parameter.
This root is nothing but a instance of TreeNode which will look something like
root = {val: 35, right: null, left: null}
right and left can point to other TreeNodes as well.
Question gives you a root node, left node and a right node
root.left points to a node which is your left node and root.right points to a node which is your right node
So suppose if my tree is something like this
root is 5, root.left would be node 3 and root.right would be node 1
root.left.val is equal to 3 and root.right.val is equal to 1 & root.val is 5
So if addition of both root.left.val & root.right.val is equal to root.val the return statement would return true else false.
So in this case it would return false as 3 + 1 is not equal to 5
And this is how below code would work :)
var checkTree = function(root) {
return root.left.val + root.right.val === root.val
};
Thanks,
I was allmost there but did not know i need .val behind left and right too.
So i was at
return root.val == (root.left + root.right);
As the .val was not in the definition for right and left.

Implementing BST backed up by Array in Javascript

I'm trying to implement bst using array, but no luck:
class BST {
constructor() {
this.array =[]
this.rootSet = false;
this.index = 0
}
getLeft(index) {
return (2 * index) + 1
}
getRight(index) {
return 2 * (index + 1)
}
getParent(index) {
return index >>> 1
}
insert(value) {
if (!this.rootSet) {
this.rootSet = true
this.array = [value]
this.index++;
} else {
// inserts recursively.
this.insertHelper(this.getParent(0), value);
}
}
// helper function to insert recursively.
insertHelper(index, value) {
if (value < this.array[index] && this.array[this.getLeft(index)] === undefined) {
this.array[this.getLeft(index)] = value
} else if (value >= this.array[index] && this.array[this.getRight(index)] === undefined) {
this.array[this.getRight(index)] = value
} else {
if (value < this.array[index]) {
this.insertHelper(this.getLeft(index), value);
} else {
this.insertHelper(this.getRight(index), value);
}
}
}
}
I tried the following:
a.insert(2)
a.insert(0)
a.insert(3)
a.insert(10)
a.insert(30)
a.array // [2, 0, 3, empty × 3, 10, empty × 7, 30]
a.array doesn't look correct. Not sure where I'm making the mistake.
Your result looks correct to me. The reason for the sparsity is that this is a typical characteristic of array-based backing structures for binary trees. If the tree isn't complete, there are wasted entries due to the empty elements representing unfilled subtrees. Since BSTs typically need balancing to retain optimal time complexity, a linked node-based approach is the typical solution, which makes rotation and balancing easier.
Typically, the array backing structure is used for binary heaps which benefit from the memory layout and speed of arrays over heap-allocated nodes and pointers; the heap operations don't permit sparsity and are easy to reason about in a linear structure using the same parent-child relationship you've used here.
Having said that, you can simplify your code considerably:
class BST {
constructor() {
this.array = [];
}
insert(value, ix=0) {
if (this.array[ix] === undefined) {
this.array[ix] = value;
}
else if (value < this.array[ix]) {
this.insert(value, ix * 2 + 1);
}
else {
this.insert(value, ix * 2 + 2);
}
}
}
/*
2
/ \
0 3
\
10
\
30
*/
const bst = new BST();
[2, 0, 3, 10, 30].forEach(e => bst.insert(e));
console.log(bst.array);
I haven't checked the code but the result looks correct to me.
The first item in the array is the root. Then the next two are the rank 1 nodes
then the next 4 are the rank 2 nodes.
then the next 8 are the rank 4 nodes
Your result should be

Recursion in right subtree

I am trying to do different problems each day in order to become more flexible writing code, yet this one stopped me in my tracks so far.
The following code is supposed to build up a binary tree from a given String in preOrder traversal. I.e., "5 3 1 N N N 7 N N" for the following binary tree. Elements are separated by spaces, N marks empty nodes, which are just null.
5
/ \
3 7
/
1
It should be as simple as walking through the split up string and when something other than "N" is found, a Node is constructed with the value, and the Index increased.
After the index was increased, I recur to put the next array element into the left subtree. If "N" is encountered, nothing needs to be done because a node's constructor already has the left child set as null, so I only increase the index. Theoretically, the right node should be assigned in the previous recursion step... yet somehow that fails?
Is it because of JavaScript's async behaviour? What did I miss?
Thank you in advance for your help!
class Node {
constructor(val) {
this.val = val;
this.left = this.right = null;
}
}
class Index {
constructor() {
this.index = 0;
}
incr() {
this.index++;
}
}
function deserialize(treeString) {
let treeArray = treeString.split(" ");
let index = new Index();
let length = treeArray.length;
function helper(index) {
if (index.index < length) {
let node = null;
if (treeArray[index.index] !== "N") {
node = new Node(parseInt(treeArray[index.index], 10));
index.incr();
node.left = helper(index);
node.right = helper(index);
} else {
index.incr();
}
return node;
};
}
return helper(index);
}
I don't get your error because console.log(JSON.stringify(deserialize('5 3 1 N N N 7 N N'),null,1)) does give me
{
"val": 5,
"right": {
"val": 7,
"right": null,
"left": null
},
"left": {
"val": 3,
"right": null,
"left": {
"val": 1,
"right": null,
"left": null
}
}
}
which seems to be your intended result
we can do a bit "simpler though"
prevent yourself from useless indentation
Especially for stops on recursive condition, it is best practice to write them first
function recurse(){
if (index.index == length) {
//stop code
return
}
//your other conditions...
//your code which will call recurse
}
2.1 your "iterator" here is useless
since you are still dependant to know your structure (treeArray must "implement" operator[]), taking a dumb variable will at least be less code
2.2 if you really want to use an iterator
you see that whether in your if or else path, you incr (which is equivalent to "advance in treeArray"). So just iterate over treeArray. Below we "pop" elements (forwardly")
function helper(arr){
if end
return
let currentNodeVal = arr.shift() //advance in treeArray
if (currentNodeVal !== "N") {
node = new Node(parseInt(currentNodeVal, 10));
node.left = helper(arr);
node.right = helper(arr);
} else {
//nothing to do
}
}
Is it because of JavaScripts async behaviour?
Here you actually don't have any asynchronous code running. If you named
helperinfty = function(){
while(true){}
}
node.left = helperinfty(arr)
node.right = helper(arr);
you could observe that node.right = helper(arr) is never executed. You get the "funky" stuff when you use setTimeout, etc (put it simply when a function needs a callback to sync flow)
(answer is no)
simplified code
prints the same tree as in beginning
class Node {
constructor(val) {
this.val = val;
this.left = this.right = null;
}
}
function deserialize(treeString) {
let arr = treeString.split(" ");
function helper(arr) {
if(arr.length == 0)return null;//note that in previous implem helper "could" return undefined...which is not something desired
let current = arr.shift();
if (current == "N"){
//this is a matter of preference. Arguably one can prefer your style.
//I prefer to emphase that when N is found it is meant to be a null node, and "abort" any recursion
return null
}
let node = new Node(parseInt(current, 10));
node.left = helper(arr);
node.right = helper(arr);
return node;
}
return helper(arr);
}
x = deserialize('5 3 1 N N N 7 N N');
console.log(JSON.stringify(x, null, 4))

Build an object while traversing a Binary Search Tree in JavaScript

The idea is to traverse a Binary Search Tree (BST) and build an object in the same method. The object represents the BST.
For example:
20
/ \
5 60
\ / \
10 40 90
\
50
\
55
I can generate this object while inserting an element to the BST, however, since I want to apply some balance algorithm into it, I won't need to insert any other node. I'll just need to get the final object with the result of the balancing.
The traverse method that came up was the Breadth-first travel. However, I didn't figure out how the object would be built in a non-recursive approach since the Breadth-first travel is an iterative method.
For the example above the result object shall be this one shown below:
{
val:20,
l:{
val:5,
l:{},
r:{
val:10,
l:{},
r:{}
}
},
r:{
val:60,
l:{
val:40,
l:{},
r:{
val:50,
l:{},
r:{
val:55,
l:{},
r:{}
}
}
},
r:{
val:90,
l:{},
r:{}
}
}
}
What I have so far is:
Breadth_first = function (callback) {
var q = [];
q.push(root);
while(q.length > 0){
var node = q.shift();
callback(node.level, node.value);
if(node.leftChild.value !== null){
q.push(node.leftChild);
}
if(node.rightChild.value !== null){
q.push(node.rightChild);
}
}
}
The current method I'm using to build the object in such format is this one below:
insertNode = function (node, value, jsObj) {
if (node.value === null) {
node.leftChild = new Node();
node.rightChild = new Node();
node.value = value;
jsObj.val = value;
return true;
}
var nodeVal = parseInt(node.value, 10);
if (value < nodeVal) {
if(!jsObj.l.val)
jsObj.l = {val:null, l:{}, r:{}};
insertNode(node.leftChild, value, jsObj.l);
} else if (value > nodeVal) {
if(!jsObj.r.val)
jsObj.r = {val:null, l:{}, r:{}};
insertNode(node.rightChild, value, jsObj.r);
}
}
Any guesses or clue would be appreciated!

Find closest nodes in binary search tree

I want to have a collection of objects, sorted by some numerical property, val. I want to be able to retrieve all objects matching where val matches some value, say, x, or if no objects exist with property val matching x, find the objects with the nearest val above and below x. (Multiple objects may have the same values for val)
One of the few BST libraries I've found is dsjslib. It has what it calls a TreeMultiMap which holds multiple values for a single key. The problem is I can't see how I could retrieve nodes closest to x if there are no exact matches.
How might I go about this? Browser support for ES6 seems to be coming a long way, so perhaps I could use its Map for this?
This sounds like you are after binary search. I had developed a binary findIndex method for Arrays which works in sorted arrays of course. It takes a callback and with the help of it a sorted array of objects can also be searched. Since it is binary search it is much faster than the standard findIndex or indexOf methods.
The default callback is x => x which makes it handle sorted primitives by default if no callback is provided. With it's current state if it finds the searched object it returns it's index but if not it will return -1. If the searched it can easily be modified to return the indices of proximity. I can add that later if you require.
Array.prototype.sortedFindIndex = function(val, cb = x => x) { // default callback for primitive arrays
var deli = this.length-1, // delta index
base = 0; // base to add the delta index
while (deli > 0 && cb(this[base + deli]) != val) {
deli = ~~(deli/2);
cb(this[base + deli]) < val && (base += deli);
}
return cb(this[base + deli]) === val ? base + deli : -1;
};
arr = [{a:0,b:"foo"},{a:1,b:"bar"},{a:2,b:"baz"},{a:3,b:"qux"},{a:4,b:"fox"},{a:5,b:"pun"},{a:6,b:"alf"}],
idx = arr.sortedFindIndex(4, o => o.a);
console.log(idx);
And here is a modified version which handles failure cases more cleverly. If the searched item can not be found, the following snippet will return a negated value of the index where it should have existed. Tricky part. It will return a negative zero (-0) if the missing item should be inserted at index 0. Now it should be a piece of cake to find the upper and lower proximity of a non existent item.
Array.prototype.sortedFindIndex = function(val, cb = x => x) { // default callback for primitive arrays
var deli = this.length-1, // delta index
base = 0, // base to add the delta index
calculatedValue = cb(this[base + deli]), // use callback and get the value
expectedAt = i => { while (this[i] !== void 0 && cb(this[i]) < val) ++i;
return -i};
while (deli > 0 && calculatedValue != val) {
deli = ~~(deli/2);
(calculatedValue = cb(this[base + deli])) < val && (base += deli);
}
return calculatedValue === val ? base + deli : expectedAt(base + deli);
};
arr = [{a:0,b:"foo"},{a:1,b:"bar"},{a:2,b:"baz"},{a:3,b:"qux"},{a:4,b:"fox"},{a:5,b:"pun"},{a:6,b:"alf"},{a:7,b:"alf"},{a:8,b:"alf"},{a:9,b:"alf"},{a:10,b:"alf"},{a:11,b:"alf"},{a:13,b:"alf"}],
idx = arr.sortedFindIndex(12, o => o.a);
console.log(idx);

Categories