Trouble finding the height of my first NON Binary Tree in JS - javascript

Here is the constructor function as well as the 'add' method...
function Tree(value) {
this.value = value;
this.children = [];
}
Tree.prototype.add = function(...value) {
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
let node = new Tree;
node.value = value[i];
this.children[this.children.length] = node;
}
return;
}
let node = new Tree;
node.value = value;
this.children[this.children.length] = node;
};
Here is the 'height' method I am working on...
Tree.prototype.height = function() {
let height = 0;
if (this.children != null) {
if (this.children.length == 0) {
return height;
} else {
for (let i = 0; i < this.children.length; i++) {
height = Math.max(height, this.height(this.children[i]));
}
return depth + 1;
}
return height;
}
};
I am unsure of why I am getting "RangeError: Maximum call stack size exceeded" because the method should return height if the children array length is 0.
Here are the test cases I am using...
tree = new Tree();
tree.add(5);
tree.add(8);
var son = tree.children[0];
son.add(1);
son.add(4);
var daughter = tree.children[0];
daughter.add(10);
var grandson = son.children[1];
grandson.add(3);
grandson.height() //should return 1
tree.height() //should return
Any tips on how to fix my infinite loop will be greatly appreciated. Thank you for your time!

Related

Javascript Recursion inside class method (this behaviour)

The problem lies inside toString function , this does not actualize the scope as the function calls itself:
I need something to solve the problem , 'this' is the worst in javascript , i have implemented the exact data structure and function in python and it works... , i've tried binding and arrow functions , maybe you can help me out...
The expected result for the code down below :
1
|__2
|__3
|__4
|__5
class Node {
constructor(data){
this.data = data;
this.parent = null;
this.children = [];
Node.nodes.push(this);
this.index = Node.nodes.length;
}
addChild(node){
node.parent = this;
this.children.push(node);
}
getLevel(){
let level = 0;
while(this.parent){
level+=1;
this.parent = this.parent.parent;
}
//console.log('lvl',level);
return level;
}
toString (){
// console.log('lvl',this.getLevel());
let prefix = " ".repeat(this.getLevel()*3);
prefix += this.getLevel()===0 ? "" :"|__";
console.log(prefix + this.index);
if(this.children){
for(let i = 0; i < this.children.length; i++){
this.children[i].toString();
}
}
}
pathToRoot(){
return 0;
}
}
Node.nodes = [];
const main = (()=>{
let root = new Node('root');
let kid1 = new Node('kid1');
let kid2 = new Node('kid2');
let kid3 = new Node('kid3');
let kid4 = new Node('kid4');
root.addChild(kid1);
kid1.addChild(kid2);
kid2.addChild(kid3);
kid3.addChild(kid4);
console.log('kid4 lvl :',kid4.getLevel())
root.toString(root);
})()
You assign the parent of the parent in a loop. Instead use a variable for looping the parents.
class Node {
constructor(data) {
this.data = data;
this.parent = null;
this.children = [];
if (!Node.nodes) Node.nodes = [];
Node.nodes.push(this);
this.index = Node.nodes.length;
}
addChild(node) {
node.parent = this;
this.children.push(node);
}
getLevel() {
let level = 0;
let parent = this.parent; // start with parent
while (parent) { // check value
level += 1;
parent = parent.parent; // assign parent
}
//console.log('lvl',level);
return level;
}
toString() {
// console.log('lvl',this.getLevel());
let prefix = " ".repeat(this.getLevel() * 3);
prefix += this.getLevel() === 0 ? "" : "|__";
console.log(prefix + this.index);
if (this.children) {
for (let i = 0; i < this.children.length; i++) {
this.children[i].toString();
}
}
}
pathToRoot() {
return 0;
}
}
const main = (() => {
let root = new Node('root');
let kid1 = new Node('kid1');
let kid2 = new Node('kid2');
let kid3 = new Node('kid3');
let kid4 = new Node('kid4');
root.addChild(kid1);
kid1.addChild(kid2);
kid2.addChild(kid3);
kid3.addChild(kid4);
console.log('kid4 lvl :', kid4.getLevel())
root.toString(root);
console.log(root);
})();
.as-console-wrapper { max-height: 100% !important; top: 0; }
It seems like it is not about the this mechanism. You lack a base case(so the recursive function will keep running forever). You need to think of in what situation the recursion should end.
toString (){
// think of your base case here. Examples are as below
// should it be prefix === ""?
// should it be when length of prefix smaller or greater than specific length
let prefix = " ".repeat(this.getLevel()*3);
prefix += this.getLevel()===0 ? "" :"|__";
console.log(prefix + this.index);
if(this.children){
for(let i = 0; i < this.children.length; i++){
this.children[i].toString();
}
}
}

Improve the performance of recursion for generating tree using JSON data

I need to construct a tree structure from data represented in JSON as object and parent relationship. I have implemented below code which is successfully doing the job but I am not sure whether it's giving the best performance (I mean doing the job in as less as possible iteration).
Please Note, the root of the tree is represented as parent is same as object. e.g. {"object":"A", "parent":"A"}
Suggestions about any other implementation with better performance would be helpful!!
var jsonInput =
[
{"object":"A", "parent":"A"},
{"object":"B", "parent":"A"},
{"object":"C", "parent":"A"},
{"object":"D", "parent":"B"},
{"object":"E", "parent":"B"},
{"object":"F", "parent":"D"},
{"object":"G", "parent":"D"},
{"object":"H", "parent":"E"},
{"object":"I", "parent":"E"},
{"object":"J", "parent":"C"},
{"object":"K", "parent":"C"},
{"object":"L", "parent":"J"},
{"object":"M", "parent":"J"},
{"object":"N", "parent":"K"},
{"object":"O", "parent":"K"},
{"object":"P", "parent":"N"},
{"object":"Q", "parent":"N"},
{"object":"R", "parent":"O"},
{"object":"S", "parent":"O"}
];
var root = getRoot();
root.childs = findChildrens(root);
console.log("The tree hierarchy is:")
console.log(root);
function getRoot() {
var root;
for (var counter = 0; counter < jsonInput.length; counter++){
var item = jsonInput[counter];
if(item.object === item.parent) {
root = item;
break;
}
}
var returnValue = JSON.parse(JSON.stringify(root));
root.visited = true;
return returnValue;
}
function findChildrens(parentObject) {
var childs = [];
for (var counter = 0; counter < jsonInput.length; counter++){
var item = jsonInput[counter];
if(item.parent === parentObject.object && !item.visited) {
var child = JSON.parse(JSON.stringify(item));
item.visited = true;
child.childs = findChildrens(child);
childs.push(child);
}
}
return childs;
}
A simpler solution with a linear runtime.
var data = [
{"object":"A", "parent":"A"},
{"object":"B", "parent":"A"},
{"object":"C", "parent":"A"},
{"object":"D", "parent":"B"},
{"object":"E", "parent":"B"},
{"object":"F", "parent":"D"},
{"object":"G", "parent":"D"},
{"object":"H", "parent":"E"},
{"object":"I", "parent":"E"},
{"object":"J", "parent":"C"},
{"object":"K", "parent":"C"},
{"object":"L", "parent":"J"},
{"object":"M", "parent":"J"},
{"object":"N", "parent":"K"},
{"object":"O", "parent":"K"},
{"object":"P", "parent":"N"},
{"object":"Q", "parent":"N"},
{"object":"R", "parent":"O"},
{"object":"S", "parent":"O"}
];
var rootNodes = data.filter(function(node) {
if (node.object in this)
throw new Error("duplicate object " + node.object);
this[node.object] = node;
node.children = [];
if (node.parent === node.object) return true;
var parent = this[node.parent];
if (!parent)
throw new Error("invalid parent " + node.parent);
parent.children.push(node);
}, Object.create(null));
console.log(rootNodes);
.as-console-wrapper {
top: 0;
max-height: 100%!important
}

Depth first search using node gives, RangeError: Maximum call stack size exceeded

I’m trying to rewrite my code to get over a stack size error “RangeError: Maximum call stack size exceeded” I run into. I’m trying to run a DFS using a stack in JavaScript using node.
I’ve heard a lot about settimeout but I’m not sure how to implement it in my case. Any advice would be great.
Heres the code that fails:
var Stack = function() {
this.items = [];
};
Stack.prototype.push = function(obj) {
this.items.push(obj);
};
Stack.prototype.pop = function() {
return this.items.pop();
};
Stack.prototype.isEmpty = function() {
return this.items.length === 0;
};
Stack.prototype.isExplored = function(n){
return this.items.indexOf(n) !== -1;
}
Stack.prototype.emptyOut = function() {
this.items = [];
return this.items.length === 0;
};
var stack = new Stack();
var adjList=[];
for(var i= 1; i<15557;i++){
adjList.push([i])
}
adjList.push([0])
function DFS(graph, s){
stack.push(s);
for(var i = 0 ; i < graph[s].length; i++){
var v = graph[s][i];
if(!stack.isExplored(v)){
DFS(graph, v);
}
}
}
DFS(adjList, 0)
console.log("done", stack);
Have you considered switching to an iterative approach?
var Stack = function() {
this.items = [];
};
Stack.prototype.push = function(obj) {
this.items.push(obj);
};
Stack.prototype.pop = function() {
return this.items.pop();
};
Stack.prototype.isEmpty = function() {
return this.items.length === 0;
};
Stack.prototype.isExplored = function(n) {
return this.items.indexOf(n) !== -1;
}
Stack.prototype.emptyOut = function() {
this.items = [];
return this.items.length === 0;
};
var stack = new Stack();
var adjList = [];
for (var i = 1; i < 15557; i++) {
adjList.push([i]);
}
adjList.push([0])
function DFS(graph, v) {
var queue = [v];
while (queue.length) {
v = queue.pop();
if (stack.isExplored(v))
continue;
stack.push(v);
for (var i = graph[v].length - 1; i >= 0; i--) {
queue.push(graph[v][i]);
}
}
}
DFS(adjList, 0);
console.log("done", stack);
The limit is now the amount of memory node has at disposal.

Hashing: Separate chaining with linked list in javascript

/*****************************************************
LIST
*****************************************************/
function Node(element) {
// store the node's data
this.element = element;
// store a link to the next node in the linked list
this.next = null;
}
function LList() {
this.head = new Node("head");
this.find = find;
this.insert = insert;
this.display = display;
this.findPrevious = findPrevious;
this.remove = remove;
}
function find(item) {
var currNode = this.head;
while(currNode.element != item) {
currNode = currNode.next;
}
return currNode;
}
function insert(newElement) {
var newNode = new Node(newElement);
var current = this.find("head");
newNode.next = current.next;
current.next = newNode;
}
function display(list) {
var currNode = this.head.next;
while (currNode != null) {
console.log(currNode.element);
currNode = currNode.next;
}
}
function findPrevious(item) {
var currNode = this.head;
while((currNode.next != null) && (currNode.next.element != item)) {
currNode = currNode.next;
}
return currNode;
}
function remove(item) {
var prevNode = this.findPrevious(item);
if (prevNode.next != null) {
prevNode.next = prevNode.next.next;
}
}
/*****************************************************
Hash Table
*****************************************************/
function HashTable() {
this.table = new Array(137);
this.betterHash = betterHash;
this.showDistro = showDistro;
this.put = put;
this.get = get;
this.buildChains = buildChains;
}
function put(key) {
var pos = this.betterHash(key);
this.table[pos].insert(key);
}
function get(key) {
var pos = this.betterHash(key);
return this.table[pos].find(key).element;
}
function betterHash(key) {
var constant = 37;
var total = 0;
for (var i = 0; i < key.length; i++) {
total += constant * total + key.charCodeAt(i);
}
total = total % this.table.length;
if(total < 0) {
total += this.table.length - 1;
}
return parseInt(total, 10);
}
function showDistro() {
for(var i = 0; i < this.table.length; i++) {
var currNode = this.table[i].head.next;
while (currNode != null) {
console.log(currNode.element);
currNode = currNode.next;
}
}
}
function buildChains() {
for (var i = 0; i < this.table.length; i++) {
this.table[i] = new LList();
}
}
var hTable = new HashTable();
hTable.buildChains();
var someNames = ["David", "Jennifer", "Donnie", "Raymond",
"Cynthia", "Mike", "Clayton", "Danny", "Jonathan"];
for(var i = 0; i < someNames.length; i++) {
hTable.put(someNames[i]);
}
hTable.showDistro();
hTable.get("David"); // "David"
I built a separate chaining by using both linked list and hash table in javascript. I was reading this book "Data structure and algorithm in javascript". But this author made a lot of mistakes and I think he doesn't really know about javascript. I modified his codes a lot. But since JavaScript is my first programming language and also this is my first time to learn data structure and algorithm, so I am not sure if I build it right.
Please explain how to modify or rebuild these codes again!

copy an object doesn't effect on my code

I suppose to copy a object source, while copy changes, sourceshould not change.The source code goes as follow:
layoutTreemap: function(source) {
var copy = jQuery.extend(true,{},source);
var select_polygon = this.get_selected_polygon();
var vt = d3.layout.voronoitreemap()
var layoutNodes = vt(copy);
return layoutNodes;
}
d3.layout.voronoitreemap = function() {
var hierarchy = d3.layout.hierarchy().sort(null),
root_polygon = [[0,0],[500,0],[500,500],[0,500]],
iterations = 100,
somenewvariable = 0;
function voronoitreemap(d, depth) {
var nodes = hierarchy(d),
root = nodes[0];
root.polygon = root_polygon;
root.site = null;
if (depth != null){
max_depth = depth;
}
else{
max_depth = "Infinity";
}
computeDiagramRecursively(root, 0);
return nodes;
}
function computeDiagramRecursively(node, level) {
var children = node.children;
if(node.parent) node.parent = null;
if (children && children.length && level < max_depth) {
node.sites = VoronoiTreemap.init(node.polygon, node); // can't say dataset, how about node?
VoronoiTreemap.normalizeSites(node.sites);
VoronoiTreemap.sites = node.sites;
VoronoiTreemap.setClipPolygon(node.polygon);
VoronoiTreemap.useNegativeWeights = false;
VoronoiTreemap.cancelOnAreaErrorThreshold = true;
var polygons = VoronoiTreemap.doIterate(iterations);
// set children polygons and sites
for (var i = 0; i < children.length; i++) {
children[i].polygon = polygons[i];
children[i].site = VoronoiTreemap.sites[i];
computeDiagramRecursively(children[i], (level + 1));
}
}
}
....
return d3_layout_hierarchyRebind(voronoitreemap, hierarchy);
}
But after execute vt(copy), the source has been changed.
This gives issues
root.polygon = root_polygon;
children[i].polygon = polygons[i];
You are copying arrays you think. but actually you are copying the reference to the array. now you have two pointers to the same object(imagine two people using the same fork at dinner)
You need to change this into
root.polygon = root_polygon.slice();
children[i].polygon = polygons[i].slice();
this way the array gets copied instead of referenced. Now each dinner guest has its own fork.

Categories

Resources