Why does parameter order matter for `this`? - javascript

I'm trying to insert a new node into a BST recursively. I'm not sure why the order of parameters matters in my method. Here's the Node class:
class Node {
constructor(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
Here's the BST class. Insert is a method on the class. Insert inserts the new node and returns the tree.
class BinarySearchTree {
constructor(root = null) {
this.root = root;
}
insert(val, current = this.root) {
if (this.root === null) {
this.root = new Node(val);
return this;
}
if (val < current.val) {
if (current.left === null) {
current.left = new Node(val);
return this;
}
return this.insert(val, current.left);
} else {
if (current.right === null) {
current.right = new Node(val);
return this;
}
return this.insert(val, current.right);
}
}
}
But when I flip the parameters, ie insert(current = this.root, val) and return this.insert(current.left, val) and return this.insert(current.right, val) it doesn't work and instead returns undefined. Why is that the case?
I pass in the parameters in the same order as the function defintion so I don't believe that's the case. I thought that maybe it had to do with hoisting but I don't understand how that could be the case either. My guess is that it maybe has to do with this?

Related

Error in Recursive Function Used For Inserting a Node in a Binary Tree in JavaScript

I am getting undefined after the node is inserted in the tree, don't know why is that happening. Here's what is happening - after the node is inserted, the function should return true, but it instead returns undefined. After inserting the node, the code doesn't stop and goes back to if check condition, sets 'current' to '7', this keeps happening until 'current' is '10'(root value) and then it finally goes back to insert() function which returns undefined. My only problem is that why is it returning undefined, and why is it going back to root after inserting the node in the desired place. Can someone please tell me? Am I missing something very small?
by the way, the value I inserted is 8. tree.insert(8);
class Node{
constructor(val){
this.val = val;
this.left = null;
this.right = null;
}
}
class BinarySearchTree{
constructor(){
this.root = null;
}
insert(val){
if(!this.root){
this.root = newNode;
return this;
}
let newNode = new Node(val);
if(val === this.root.val)return false;
function recursiveInsert(current,newNode){
if(newNode.val === current.val)return false;
if(newNode.val > current.val){
if(!current.right){
current.right = newNode;
return true;
}
recursiveInsert(current.right,newNode);
}
if(newNode.val<current.val){
if(!current.left){
current.left = newNode;
return true;
}
recursiveInsert(current.left, newNode);
}
}
return recursiveInsert(this.root,newNode);
}
}
let tree = new BinarySearchTree();
tree.root = new Node(10);
tree.root.left = new Node(7);
tree.root.right = new Node(15);
tree.root.left.right = new Node(9);
There are these issues:
In the if(!this.root){ block, you reference newNode before it has received a value, so move the initialisation of newNode before this if statement.
In the same block you return this, but you write that you want to return a boolean, so change that to return true;
The recursive calls should be used to return whatever that call returned, so prefix them with return, just like you have done in the last line where you made that initial call.
Demo
I would also suggest building the initial tree with the insert method. And add an iterator so you can have a quick output of your tree in inorder sequence.
Note that there is an else if condition that is always true, so just use else:
class Node {
constructor(val) {
this.val = val;
this.left = null;
this.right = null;
}
// Add this method for inorder traversal of the values
* inorder() {
if (this.left) yield * this.left.inorder();
yield this.val;
if (this.right) yield * this.right.inorder();
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(val) {
let newNode = new Node(val); // First create the node
if (!this.root) {
this.root = newNode;
return true; // you wanted a boolean
}
// This is not necessary: it already happens in the function below
// if(val === this.root.val)return false;
function recursiveInsert(current,newNode) {
if (newNode.val === current.val) return false;
if (newNode.val > current.val) {
if (!current.right) {
current.right = newNode;
return true;
}
// Return the result
return recursiveInsert(current.right, newNode);
} else { // it is the only possibility left
if (!current.left) {
current.left = newNode;
return true;
}
// Return the result
return recursiveInsert(current.left, newNode);
}
}
return recursiveInsert(this.root, newNode);
}
}
let tree = new BinarySearchTree();
// Build the tree only with the insert-method
tree.insert(10);
tree.insert(7);
tree.insert(15);
tree.insert(9);
tree.insert(8); // Add the value you wanted to test with
tree.insert(15); // Try some duplicate
console.log(...tree.root.inorder());
You can make the code still a bit nicer by making the recursive insert function a method of the Node class. Also, enrich the BinarySearchTree constructor, so that it can get a number of values to start with, much like the native Array constructor works.
class Node {
constructor(val) {
this.val = val;
this.left = null;
this.right = null;
}
* inorder() {
if (this.left) yield * this.left.inorder();
yield this.val;
if (this.right) yield * this.right.inorder();
}
insert(val) {
if (val === this.val) return false;
if (val > this.val) {
if (this.right) return this.right.insert(val);
this.right = new Node(val);
} else {
if (this.left) return this.left.insert(val);
this.left = new Node(val);
}
return true;
}
}
class BinarySearchTree {
constructor(...values) {
this.root = null;
for (let val of values) this.insert(val);
}
* inorder() {
if (this.root) yield * this.root.inorder();
}
insert(val) {
if (this.root) return this.root.insert(val);
this.root = new Node(val);
return true;
}
}
let tree = new BinarySearchTree(10, 7, 15, 9);
tree.insert(8);
tree.insert(15);
console.log(...tree.inorder());
You should return the recursiveCall or you will get undefined because the recursive call take time to execute and the function won't wait for it to return something.
// ...code
return recursiveInsert(current.right,newNode);
// and
return recursiveInsert(current.left,newNode);

Can you spot a mistake in this construction of a Binary Search Tree?

I am going through AlgoExpert and learning about Binary Search trees and their construction for the first time. The below implementation seems to be working locally for me as expected but I am getting errors for most test cases on AlgoExpert. This is my implementation:
class BST {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
insert(value) {
const searchTree = (node) => {
if (value < node.value) {
if (!node.left) {
node.left = new BST(value);
} else {
searchTree(node.left);
}
} else {
if (!node.right) {
node.right = new BST(value);
} else {
searchTree(node.right);
}
}
};
searchTree(this);
// Write your code here.
// Do not edit the return statement of this method.
return this;
}
contains(value) {
let currentNode = this;
while (currentNode) {
if (currentNode.value === value) {
return true;
}
if (value < currentNode.value) {
currentNode = currentNode.left;
} else {
currentNode = currentNode.right;
}
}
return false;
// Write your code here.
}
min() {
let currentNode = this;
while (currentNode.left) {
currentNode = currentNode.left;
}
return {
value: currentNode.value,
node: currentNode
};
}
remove(value) {
let nodeToBeRemoved = this;
while (nodeToBeRemoved.value !== value) {
if (value < nodeToBeRemoved.value) {
nodeToBeRemoved = nodeToBeRemoved.left;
} else {
nodeToBeRemoved = nodeToBeRemoved.right;
}
}
if (!nodeToBeRemoved.right) {
nodeToBeRemoved.value = null;
} else {
const {
value: minValue,
node
} = nodeToBeRemoved.right.min();
node.value = null;
nodeToBeRemoved.value = minValue;
}
// Write your code here.
// Do not edit the return statement of this method.
return this;
}
}
Can you see any mistake that could cause this error on Algo Expert? I don't see where the mistake is. This is the error I am getting:
Cannot read property 'toString' of null
TypeError: Cannot read property 'toString' of null
at constructBinaryTreeWithUniqueIds
at constructBinaryTreeWithUniqueIds
...
The remove method has some issues:
After the first while loop, nodeToBeRemoved could be null. In that case the next if statement will make an invalid reference with nodeToBeRemoved.right.
nodeToBeRemoved.value = null does not remove a node. It just modifies the value of a node to null. But it is still part of the tree, and could even have a left child.
The same is true for node.value = null. This remains part of the tree and could have a right child.
remove(value) {
let nodeToBeRemoved = this;
if (value < this.value) {
if (this.left) this.left = this.left.remove(value);
} else if (value > this.value) {
if (this.right) this.right = this.right.remove(value);
} else if (!this.right) {
return this.left;
} else if (!this.left) {
return this.right;
} else {
const minValue = this.right.min().value;
this.right.remove(minValue); // use recursion to delete that min-node
this.value = minValue;
}
return this;
}

How to use recursion function to traverse tree in Javascript

I was building a tree traverse function and it has to use recursion.
What I want the output to be is
'{"value":9,"left":{"value":4,"left":{"value":1},"right":{"value":6}},"right":{"value":20,"left":{"value":15},"right":{"value":170}}}'
Could someone figure out how to use recursion in the traverse function to get the output?
Here is my JS:
class Node {
constructor(value){
this.left = null;
this.right = null;
this.value = value;
}
}
class BinarySearchTree {
constructor(){
this.root = null;
}
insert(value){
const newNode = new Node(value);
if (this.root === null) {
this.root = newNode;
} else {
let currentNode = this.root;
while(true){
if(value < currentNode.value){
//Left
if(!currentNode.left){
currentNode.left = newNode;
return this;
}
currentNode = currentNode.left;
} else {
//Right
if(!currentNode.right){
currentNode.right = newNode;
return this;
}
currentNode = currentNode.right;
}
}
}
}
}
const tree = new BinarySearchTree();
tree.insert(9)
tree.insert(4)
tree.insert(6)
tree.insert(20)
tree.insert(170)
tree.insert(15)
tree.insert(1)
JSON.stringify(traverse(tree.root))
// 9
// 4 20
//1 6 15 170
function traverse(node) {
const tree = { value: node.value };
tree.left=node.left
if(node.left===null) {
return null
}else{
traverse(node.left);
}
tree.right=node.right
if(node.right===null) {
return null
}else{
traverse(node.right);
}
}
You're on the right track; I see that you need specially-formatted objects in your result string. I recommend starting by checking whether the current node is null; if so, return nothing (this key will be ignored by the calling function). Otherwise, create a node object to prepare for return and traverse left and right children. After traversing both subtrees recursively, return the root node. This will build the result structure up from the leaves and end with the root.
In your code,
if(node.left===null) {
return null
is a bit premature, for example. If node has a right subtree, we don't want to abandon traversing it. It's also necessary to return something to the caller in all cases except for empty children.
Also, you may consider putting traverse in the BinaryTree class and have it operate on its member field; I left it as-is in the below example.
Lastly, this is a pre-order traversal; visit the root first, then the left and right subtrees.
function traverse(node) {
if (node) {
const left = traverse(node.left);
const right = traverse(node.right);
return {
value: node.value,
[left&&"left"]: left,
[right&&"right"]: right
};
}
}
class Node {
constructor(value, left=null, right=null) {
this.value = value;
this.left = left;
this.right = right;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(value) {
const newNode = new Node(value);
if (this.root === null) {
this.root = newNode;
}
else {
let currentNode = this.root;
while (true) {
if (value < currentNode.value) {
if (!currentNode.left) {
currentNode.left = newNode;
return this;
}
currentNode = currentNode.left;
}
else {
if (!currentNode.right) {
currentNode.right = newNode;
return this;
}
currentNode = currentNode.right;
}
}
}
}
}
const tree = new BinarySearchTree();
tree.insert(9);
tree.insert(4);
tree.insert(6);
tree.insert(20);
tree.insert(170);
tree.insert(15);
tree.insert(1);
console.log(JSON.stringify(traverse(tree.root)));
console.log(traverse(tree.root));
// 9
// 4 20
// 1 6 15 170

Writing a recursive add method for a Javascript Binary Search Tree

I'm trying to write an add/insert method for a BST in JS, but can't seem to get it working for some reason. My code is here:
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
function BinarySearchTree() {
this.root = null;
this.add = function(insertElem){
let currNode = this.root;
var recurInsert = function(elem, node){
if(node == null){
let newNode = new Node(elem);
node = newNode;
console.log("firstNode");
return undefined;
}
else if(elem == node.value){
console.log("equal val");
return null
}
else if(elem > node.value){
console.log("go right");
recurInsert(elem, node.right);
}
else{
console.log("go left");
recurInsert(elem, node.left);
}
}
recurInsert(insertElem, currNode);
}
}
Specifically, the line node = newNode doesn't actually set node to newNode. I suspect that this could have something to do with pass-by-value nature of JavaScript, but I'm not completely sure.
Where did I go wrong?
Also, I'm hoping to keep this recursive form for now if possible.
Basically you need to hand over the object reference to the recursive function, because if not, you create new nodes without linking to the root node.
This code takes an object and the direction as key and checkes the four different dicision to make. If a new node has to be assigned, the object and the key is used.
If a value is smaller or greater than the node's value, the node is used along with the new direction for checking.
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
function BinarySearchTree() {
this.root = null;
this.add = function (value) {
function check(node, direction) {
if (node[direction] === null) {
node[direction] = new Node(value);
console.log('new node', value);
return;
}
if (node[direction].value === value) {
console.log('equal value', value);
return;
}
if (node[direction].value > value) {
console.log('go left', node[direction].value);
check(node[direction], 'left');
return;
}
if (node[direction].value < value) {
console.log('go right', node[direction].value);
check(node[direction], 'right');
}
}
check(this, 'root');
};
}
var tree = new BinarySearchTree;
tree.add(10);
tree.add(5);
tree.add(15);
tree.add(2);
tree.add(4);
tree.add(11);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
An even shorter approach by using a default node.
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
function BinarySearchTree() {
this.root = null;
this.add = function (value) {
function check(node) {
if (node.value === value) {
return;
}
if (node.value > value) {
check(node.left = node.left || new Node(value));
return;
}
if (node.value < value) {
check(node.right = node.right || new Node(value));
}
}
check(this.root = this.root || new Node(value));
};
}
var tree = new BinarySearchTree;
tree.add(10);
tree.add(5);
tree.add(15);
tree.add(2);
tree.add(4);
tree.add(11);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Small example of changing objects vs properties
function assign(o) { // take object reference as value of o
o = { bar: 43 }; // assign a new value to o, object keeps it value
console.log('o', o); // the reference to object is replaced by an own object
}
function change(o) { // take object reference as value of o
o.bar = 41; // take object reference and assign a new property
console.log('o', o); // because of the object reference, o and object has changed
}
var object = { foo: 42 };
console.log('object', object);
assign(object);
console.log('object', object);
change(object);
console.log('object', object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The problem is that you need to set the node.right or node.left to the newNode and not node = newNode. Otherwise there is no linking of references and your root will never have any children.
So your insertions should actually be done here, if right.next or left.next is null, rather than on the next recursion.
else if(elem > node.value){
console.log("go right");
if (node.right == null)
node.right = new Node(elem);
else
recurInsert(elem, node.right);
}
else{
console.log("go left");
if (node.left == null)
node.left = new Node(elem);
else
recurInsert(elem, node.left);
}
You can also remove the whole if (node == null) { ... } section, and simply check if the root is null once before starting
if (root == null) {
root = new Node(insertElem);
return null;
}
Here is the full code:
function Node(value) {
this.value = value;
this.right = null;
this.left = null;
}
function BinarySearchTree() {
this.root = null;
this.add = function(value) {
if (this.root == null) {
this.root = new Node(value);
return;
}
var recInsert = function(value, node) {
if (value == node.value) {
print("equal");
return;
}
if (value < node.value) {
if (node.left == null)
node.left = new Node(value);
else
recInsert(value, node.left);
}
else {
if (node.right == null)
node.right = new Node(value);
else
recInsert(value, node.right);
}
}
recInsert(value, this.root);
}
this.print = function() {
if (this.root != null) {
var rec = function(node) {
if (node == null)
return;
rec(node.left);
print(node.value);
rec(node.right);
}
rec(this.root);
}
}
}

TypeError: 'addNode' is not a function

I wrote a simple JavaScript p5 app (with p5.js) to create a BST data structure. When running this with Firefox, it's showing me TypeError: this.root.addNode is not a function
Can anyone please help? This is the full code (error is in line 20)
var tree;
function setup() {
noCanvas();
tree = new Tree();
tree.addValue(5);
tree.addValue(3);
console.log(tree);
}
function Tree() {
this.root = null;
}
Tree.prototype.addValue = function (val) {
var n = new Node(val);
if (this.root == null) {
this.root = n;
} else {
this.root.addNode(n);
}
}
Tree.prototype.addNode = function (n) {
if (n.value < this.value)
if (this.left == null) {
this.left = n;
} else {
this.left.addNode(n);
}
} else if (n.value > this.value) {
if (this.right == null) {
this.right = n;
} else {
this.right.addNode(n);
}
}
}
function Node(val) {
this.value = val;
this.left = null;
this.right = null;
}
root is simply a property that you later assign to be a Node. You assign the prototype function addNode to Tree, Node doesn't have that. Either set
this.root = new Tree();
or assign the prototype method addNode to Node instead.
If you are interested, this is how you might implement it using class keyword which handles a lot of manual prototype stuff handling for you so it is a little less error prone.
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
class Tree {
constructor() {
this.root = null;
}
_insert(target, key) {
if (target.key > key) {
if (!target.left) {
target.left = new Node(key);
} else {
this._insert(target.left, key);
}
} else {
if (!target.right) {
target.right = new Node(key);
} else {
this._insert(target.right, key);
}
}
}
insert(key) {
if (!this.root) {
this.root = new Node(key);
} else {
this._insert(this.root, key);
}
}
}

Categories