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.
Related
I have a circular singly linked list code:
class Node{
constructor(value){
this.value = value;
this.next = null;
}
}
class LinkdeList{
constructor(){
this.first = null;
this.last = null;
}
empty(){
return this.first === null
}
insert(value){
let newest = new Node(value);
if (this.empty()) {
this.first = this.last = newest;
this.last.next = this.first;
}else{
newest.next = this.first;
this.first = newest;
this.last.next = this.first;
}
}
traverse(){
let aux = this.first;
while (aux.next != this.first) {
console.log(aux.value);
aux = aux.next;
}
}
}
let linked = new LinkdeList();
linked.insert("David");
linked.insert("John");
linked.insert("Adam")
linked.insert("Bob");
linked.traverse();
And when I tried to print the list, I just get in console 3 names:
Bob
Adam
John
And as you can see I push 4 names in my linked list. I tried to print the values of my list in the traverse method, but It didn´t work because I don´t get in console:
Bob
Adam
John
David
The loop stops one step too early. This is a good case for a do ... while loop. You should also protect it from failing when the list is empty
traverse() {
if (this.empty()) return; // <---
let aux = this.first;
do {
console.log(aux.value);
aux = aux.next;
} while (aux != this.first);
}
Some other remarks on your code:
As in a non-empty circular list it is always true that the head follows after the tail, it is actually not needed to maintain a first reference. Just keep a last reference, knowing that you can always get the head of the list via last.next.
console.log should not be used in a class method for anything else than debugging. Give your traverse method more flexibility by making it a generator. That way you leave the decision of what to do with the values to the caller of that method.
As in a circular list a node should never have a next property with a null value, don't assign null in the Node constructor. Instead give it a self-reference.
Name the empty method isEmpty as it more clearly indicates that this will not empty the list, but will return whether it is empty.
Fix a typo in the class name: LinkedList
class Node {
constructor(value) {
this.value = value;
this.next = this; // self-reference
}
}
class LinkedList {
constructor() {
this.last = null; // No need for a `first`
}
isEmpty() {
return this.last === null;
}
insert(value) {
const newest = new Node(value);
if (!this.isEmpty()) {
newest.next = this.last.next;
this.last.next = newest;
}
this.last = newest;
}
*traverse() { // Generator
if (this.isEmpty()) return; // Guard
let aux = this.last;
do {
aux = aux.next;
yield aux.value; // Don't print. Yield instead.
} while (aux != this.last);
}
}
const linked = new LinkedList();
linked.insert("David");
linked.insert("John");
linked.insert("Adam")
linked.insert("Bob");
// Caller of traverse can decide what to do: we want to print:
for (const name of linked.traverse()) console.log(name);
Your code works perfectly fine! You just need to tweak your traversal() method because the while loop breaks before it gets a chance to log the last node.
You can try something like this:
traverse(){
let aux = this.first;
while (true) {
console.log(aux.value);
aux = aux.next;
if (aux == this.first) {
break;
}
}
}
I will expand an attribute (count)
constructor() {
...
this.count = 0;
}
Calculate it when insert is called
insert(value) {
...
this.count = this.count + 1;
}
If there is an extension removal method later, remember to calculate it
remove() {
...
this.count = this.count - 1;
}
And adjust the conditional expression of traverse,
replace while (aux.next != this.first) with for (let i = this.count; i > 0; i--)
I prefer trincot's answer, my answer is aimed at a small scope of
code changes.
In practice I will design it with a similar structure(trincot's answer).
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
class LinkdeList {
constructor() {
this.count = 0;
this.first = null;
this.last = null;
}
empty() {
return this.first === null
}
insert(value) {
let newest = new Node(value);
if (this.empty()) {
this.first = this.last = newest;
this.last.next = this.first;
} else {
newest.next = this.first;
this.first = newest;
this.last.next = this.first;
}
this.count = this.count + 1;
}
traverse() {
let aux = this.first;
for (let i = this.count; i > 0; i--) {
console.log(aux.value);
aux = aux.next;
}
}
}
let linked = new LinkdeList();
linked.insert("David");
linked.insert("John");
linked.insert("Adam")
linked.insert("Bob");
linked.traverse();
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.
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).
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.
I have a LinkedList class and I want to make the head member of the instance private. I see how I could create a key id for each instance and those hide the member of users of the LinkedList class. Looking for some other way to make this.head private
function LinkedList() {
this.head = null;
};
LinkedList.prototype = (function () {
function reverseAll(current, prev) {
if (!current.next) { //we have the head
this.head = current;
this.head.next = prev;
}
var next = current.next;
current.next = prev;
reverseAll(next, current);
};
return {
constructor: LinkedList,
reverse: function () {
reverseAll(this.head, null);
},
head: function() {
return this.head;
}
}
})();
LinkedList.prototype.add = function(value) {
var node = {
value: value,
next: null
};
var current;
if (this.head === null) {
this.head = node;
} else {
current = this.head;
while (current.next) {
current = current.next;
}
current.next = node;
}
return node;
}
LinkedList.prototype.remove = function(node) {
var current, value = node.value;
if (this.head !== null) {
if (this.head === node) {
this.head = this.head.next;
node.next = null;
return value;
}
//find node if node not head
current = this.head;
while (current.next) {
if (current.next === node) {
current.next = node.next;
return value;
}
current = current.next;
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(function() {
var obj = new LinkedList();
for (var i = 1; i <= 10; i++) {
obj.add(i);
}
console.log(obj.head);
obj.head = 'pwned';
console.log(obj.head);
});
</script>
The only way I can think of is some sort of abomination like this:
function LinkedList() {
let head = null;
// Instead of using a prototype, you attach the methods directly to this.
// This makes the methods unique per instance, so you can attach privately
// scoped variables (like head).
this.reverse = function() {
// You may use head here.
};
this.head = function() { ... };
}
However, this is highly inefficient as you'll create an entirely new set of closures with each invocation.
Even solutions like the module pattern (or the revealing module pattern) suffer from this problem. If you aren't creating too many of this object, the above example might be for you.