I am trying to do a Q20-ish game, the problem is when I try to invoque a class in Javascript. See the extract below.
var TreeQuestions = require('./Tree.js');
var node = {
value: null,
id: null,
ifAnswer:false,
No: null,
Yes: null};
var counter = 0;
node.value= "Root";
node.id=counter;
node.No=null;
nodeYes=null;
TreeQuestions.addYes(node.value,node.id,null);
counter = counter + 1 ;
...
On TreeQuestions.addYes an error comes up telling me that the method is undefined.
See my Tree.js file below for further reference.
var Tree = {
//If you need to add the root node, just specify the third parameter as null on any of addYes or addNo function;
addYes: function(value,id,CurrentNode){
//create a new item object, place data in
var node = {
value: value,
id: id,
ifAnswer:false,
No: null,
Yes: null};
//special case: no items in the tree yet
if (this._root === null){
this._root = node;
} else {
CurrentNode.Yes = node;
}
},
addNo: function(value,id,CurrentNode){
//create a new item object, place data in
var node = {
value: value,
id: id,
ifAnswer:false,
No: null,
Yes: null};
//special case: no items in the tree yet
if (this._root === null){
this._root = node;
} else {
CurrentNode.No= node;
}
},
getNo: function(CurrentNode){
if(CurrentNode.No != null){
return CurrentNode.No;
}else{
return null;
}
},
getYes: function(CurrentNode){
if(CurrentNode.Yes != null){
return CurrentNode.Yes;
} else{
return null;
}
},
gameBegin: function(){
currentNode = this._root;
var response = Yes;
var win = false;
while(currentNode != null && win==false){
if( response=="yes"){
currentNode = This.getYes(this.currentNode);
console.log(currentNode.value+" "+ currentNode.value+" "+currentNode.ifAnswer.toString())
} else if(response=="no"){
currentNode = This.getNo(this.currentNode);
console.log(currentNode.value+" "+ currentNode.value+" "+currentNode.ifAnswer.toString())
}
}
}
};
var node = {
value: null,
id: null,
ifAnswer:false,
No: null,
Yes: null
};
function Tree() {
this._root = null;
}
Which seem to be the error? Am I taking a wrong approach to this problem?
You have:
var Tree = {
And then at the end of Tree.js you have:
function Tree() {
this._root = null;
}
It means you have just overridden your Tree object with a function definition.
Also you need to export the literal which is currently assigned to var Tree:
module.exports = {
addYes:...
}
getNo: function(CurrentNode){
if(CurrentNode.No != null){
return CurrentNode.No;
} else{
return null;
}
},`getNo: function(CurrentNode){
if(CurrentNode.No != null){
return CurrentNode.No;
} else {
return null;
}
},
Related
Im trying to build an AVL/Binary tree, but was not able to find a lot of code examples, only theory. My implementation for some reason has the AVL rotation function losing half the tree when the tree is 1 sided.
Here is my code:
function buildTree(dataSet){
let root = null
function rotateRight(node){
return rotate(node,true)
}
function rotateLeft(node){
return rotate(node,false)
}
function rotate(node,right){
const inputNodeSide = right ? "left" : "right"
const targetNodeSide = right ? "right" : "left"
const targetNode = node[ inputNodeSide]
const targetNodeChild = targetNode[targetNodeSide]
targetNode[targetNodeSide] = node
node[ inputNodeSide] = targetNodeChild
return targetNode // as this is at the top
}
function createNode(data){
return {
data,
left : null,
right : null,
get balance(){
return workOutHeight(this.left) -
workOutHeight(this.right)
},
get height(){
return workOutHeight(this)
},
}
} // END createNode
function workOutHeight(node){
if(null === node){
return -1
}
return Math.max(workOutHeight(node.left),
workOutHeight(node.right))+1
}
function avl(node){
const balanced = node.balance
if(2 === balanced){
if(0 > node.left.balance){
node.left = rotateLeft(node.left);
}
return rotateRight(node);
} else if(-2 === balanced){
if(0 < node.right.balance){
node.right = rotateRight(node.right);
}
return rotateLeft(node);
}
return node
}
this.add = function(data, perent){
perent = perent || root;
if(null === perent){
return root = createNode(data);
} else if(data < perent.data){
if(null === perent.left){
return perent.left = createNode(data);
}
this.add(data,perent.left)
avl(perent)
} else if(data > perent.data){
if(null === perent.right){
return perent.right = createNode(data);
}
this.add(data,perent.right)
avl(perent)
}
} // END addData
this.tree = function(){
return JSON.parse(JSON.stringify(root))
}
if(Array.isArray(dataSet)){
dataSet.forEach(val=>this.add(val))
}
} // END buildTree
console.log(new buildTree([2,6,9,4,7,0]).tree())
Thanks for any help on this.
The problem is in the this.add method.
It calls avl ignoring the node that is returned by that call. This returned node should replace the node that was passed as argument. As you don't know where this returned node should be attached to, you should return it as the return value for this.add, and that means you should redesign that whole function to deal with this "return" feature.
Here is how it could be adapted:
this.add = function(data, perent=root) {
if (!perent) {
return createNode(data);
} else if(data < perent.data) {
perent.left = this.add(data, perent.left);
} else if(data > perent.data) {
perent.right = this.add(data, perent.right);
}
return avl(perent);
}
Note that I removed the assignment to root here. I would suggest to move that to the place where you make the initial call to this.add:
if (Array.isArray(dataSet)) {
dataSet.forEach(val => root=this.add(val));
}
And now it will work.
Redesign
As JavaScript has since ECMAScript 2015 the class syntax, I would actually make use of that, and create a Tree and Node class. Private members can be created with the # syntax.
Also, it is not efficient to calculate the height of a node every moment you need to calculate the balance factor of a node. The nice thing of AVL trees is that when you know the balance factors of the nodes that are being rotated, you can derive from that their new balance factors -- without needing the actual heights.
Here is all of that put in code, with in addition an input box, with which you can add nodes to the tree. The tree is displayed with simple indentation format (with the root at the left side).
class Tree {
#root = null;
static #config = {
"2": { "0": [ 1, -1], "1": [ 0, 0], "2": [-1, 0] },
"-2": { "0": [-1, 1],"-1": [ 0, 0],"-2": [ 1, 0] },
"1": {"-1": [ 0, -2], "0": [ 0, -1], "1": [-1, -1] },
"-1": { "1": [ 0, 2], "0": [ 0, 1],"-1": [ 1, 1] },
};
static #Node = class {
constructor(data) {
this.data = data;
this.left = this.right = null;
this.balance = 0;
}
add(data) {
const side = data < this.data ? "left" : "right";
let delta = data < this.data ? -1 : 1;
if (!this[side]) {
this[side] = new Tree.#Node(data);
} else {
let balance = Math.abs(this[side].balance);
this[side] = this[side].add(data);
if (balance || !this[side].balance) delta = 0;
}
this.balance += delta;
return this.avl();
}
avl() {
const balanced = this.balance;
if (-2 === balanced) {
if (0 < this.left.balance) {
this.left = this.left.rotateLeft();
}
return this.rotateRight();
} else if (2 === balanced) {
if (0 > this.right.balance) {
this.right = this.right.rotateRight();
}
return this.rotateLeft();
}
return this;
}
rotateRight() {
return this.rotate("left", "right");
}
rotateLeft() {
return this.rotate("right", "left");
}
rotate(inputSide, targetSide) {
const target = this[inputSide];
const child = target[targetSide];
target[targetSide] = this;
this[inputSide] = child;
// Use a lookup table to determine how the balance is impacted by rotation
[this.balance, target.balance] = Tree.#config[this.balance][target.balance];
return target;
}
toString(indent="\n") {
return (this.right?.toString(indent + " ") ?? "")
+ indent + this.data
+ (this.left?.toString(indent + " ") ?? "");
}
}
constructor(...values) {
this.add(...values);
}
add(...values) {
for (const data of values) {
this.#root = this.#root?.add(data) ?? new Tree.#Node(data);
}
}
toString() {
return this.#root?.toString() ?? "<empty>";
}
}
const tree = new Tree;
// I/O handling
document.querySelector("button").addEventListener("click", () => {
const input = document.querySelector("input");
tree.add(+input.value);
input.value = "";
input.focus();
document.querySelector("pre").textContent = tree;
});
<input type="number"><button>Add</button><br>
<pre></pre>
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;
}
I was building a BinarySearchTree by using javascript
but the insert function in my code deoesn't run what i want
my ideal result is
{"value":9,"left":{"value":4,"left":{"value":1,"left":null,"right":null},"right":{"value":6,"left":null,"right":null}},"right":{"value":20,"left":{"value":15,"left":null,"right":null},"right":{"value":170,"left":null,"right":null}}}
but my result is
'{"value":9,"left":null,"right":{"left":null,"right":{"left":null,"right":{"left":null,"right":{"left":null,"right":{"left":null,"right":{"left":null,"right":null}}}}}}}'
I think the problem is in my insert function
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){
var root=this.root
if(!root){
this.root=new Node(value)
}else{
var currentNode=root;
var newNode=new Node(value)
while(currentNode){
if(value<currentNode.value){
if(!currentNode.left){
currentNode.left=new Node;
break;
}
else{
currentNode=currentNode.left;
}
}else{
if(!currentNode.right){
currentNode.right=new Node;
break;
}else{
currentNode=currentNode.right
}
}
}
}
}
lookup(value){
var root=this.root
var searchNode = new Node(value)
if(!this.root){
return null;
}
if(searchNode===root){
return root
}else{
if(searchNode.value>root){
return root.right.value
}else{
return root.left.value
}
}
}
}
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 === null ? null : traverse(node.left);
tree.right = node.right === null ? null : traverse(node.right);
return tree;
}
Your Node class was instantiated as newNode so change all new Node into newNode *except for the first instance of new Node of course.
var newNode=new Node(value) // Leave this one alone and fix the rest
var searchNode = new Node(value) // Leave this one alone as well
var insertNode = function(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
insertNode(node.right, newNode);
}
}
};
Problem
I'm trying to study better the tree theory, i can add one or more node without problems but i noticed that if i tried to launch multiple add function without pass a parameter, simply it doesn't work. Could you explain where is my mistake ?
Code
BinarySearchTree.prototype.makeNode = function(value) {
var node = {};
node.value = value;
node.left = null;
node.right = null;
return node;
};
BinarySearchTree.prototype.add = function(value) {
var currentNode = this.makeNode(value);
if (!this.root) {
this.root = currentNode;
} else {
this.insert(currentNode);
}
return this;
};
BinarySearchTree.prototype.insert = function(currentNode) {
var value = currentNode.value;
var traverse = function(node) {
if (value > node.value) {
if (!node.right) {
node.right = currentNode;
return;
} else traverse(node.right);
} else if (value < node.value) {
if (!node.left) {
node.left = currentNode;
return;
} else traverse(node.left);
}
};
traverse(this.root);
};
Now if i try to
var bst = new BinarySearchTree();
bst.add(3).add(2);
console.log(bst);
i will have this console.log
if i try to pass an undefined value
var bst = new BinarySearchTree();
bst.add().add(2);
console.log(bst);
Expectation
I expect that the last console.log doesn't lost the value of 2.
. i read this post to understand better what if i didn't pass any value to a function
What happens if I don't pass a parameter in a Javascript function?
and other posts ( like medium and stack overflow ) and guide related to the tree theory but i didn't find any solution
SOLUTION
Thanks to the recommendation and the correction of #Nina Scholz i just added this lines to this function
BinarySearchTree.prototype.add = function(value) {
if (typeof value == 'undefined') {
value = null;
}
var currentNode = this.makeNode(value);
if (!this.root) {
this.root = currentNode;
console.log('sei qui')
} else {
this.insert(currentNode);
}
return this;
};
Nothing happens, because both conditions evaluate with undefined as value to false.
if (value > node.value) {
// ...
} else if (value < node.value) {
// ...
}
function BinarySearchTree() {}
BinarySearchTree.prototype.makeNode = function(value) {
var node = {};
node.value = value;
node.left = null;
node.right = null;
return node;
};
BinarySearchTree.prototype.add = function(value) {
var currentNode = this.makeNode(value);
if (!this.root) {
this.root = currentNode;
} else {
this.insert(currentNode);
}
return this;
};
BinarySearchTree.prototype.insert = function(currentNode) {
var value = currentNode.value;
var traverse = function(node) {
if (value > node.value) {
if (!node.right) {
node.right = currentNode;
return;
} else traverse(node.right);
} else if (value < node.value) {
if (!node.left) {
node.left = currentNode;
return;
} else traverse(node.left);
}
};
traverse(this.root);
};
var bst = new BinarySearchTree();
bst.add(3).add(2).add();
console.log(bst);
I am trying to create tree like component,
for the first level data is coming from the server ,
if the user clicks the node i need to populate the child nodes with the data from service call.
what is the best way to save the data for this tree component ?
because user will do some operations on the tree component like remove, add & move. Finally i need to send the updated data to the server .
This is the hashmap functionality I use in javascript.
I based it off the docs of java 7 hashmap.
http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
I added the load and save variables to allow JSON storage. be careful though. if you stored any complex objects(like a hashmap in a hashmap) you will lose that.
You'd have to implement your own object instantiatiors in the load and save function.
A JSfiddle to play with if you like:
http://jsfiddle.net/mdibbets/s51tubm4/
function HashMap() {
this.map = {};
this.listsize = 0;
}
HashMap.prototype._string = function(key) {
if(typeof key.toString !== 'undefined') {
return key.toString();
}
else {
throw new Error('No valid key supplied. Only supply Objects witha toString() method as keys');
}
}
HashMap.prototype.put = function(key,value) {
key = this._string(key);
if(typeof this.map[key] === 'undefined') {
this.listsize++;
}
this.map[key] = value;
}
HashMap.prototype.get = function(key) {
key = this._string(key);
return this.map[key];
}
HashMap.prototype.containsKey = function(key) {
key = this._string(key);
return !(this.map[key] === 'undefined');
}
HashMap.prototype.putAll = function(hashmap) {
if(hashmap instanceof HashMap) {
var othermap = hashmap.map;
for(var key in othermap) {
if(othermap.hasOwnProperty(key)) {
if(typeof this.map[key] === 'undefined') {
this.listsize++;
}
this.map[key] = othermap[key];
}
}
}
else {
throw new Error('No HashMap instance supplied');
}
}
HashMap.prototype.remove = function(key) {
key = this._string(key);
var ret = null;
if(typeof this.map[key] !== 'undefined') {
ret = this.map[key];
delete this.map[key];
this.listsize--;
}
return ret;
}
HashMap.prototype.clear = function() {
this.map = {};
this.listsize = 0;
}
HashMap.prototype.containsValue = function(value) {
for(var key in this.map) {
if(this.map.hasOwnProperty(key)) {
if(this.map[key] === value) {
return true;
}
}
}
return false;
}
HashMap.prototype.clone = function() {
var ret = new HashMap();
ret.map = this.map;
ret.listsize = this.listsize;
return ret;
}
HashMap.prototype.entrySet = function() {
return this.map;
}
HashMap.prototype.keySet = function() {
var ret = [];
for(var key in this.map) {
if(this.map.hasOwnProperty(key)) {
ret.push(key);
}
}
return ret;
}
HashMap.prototype.values = function() {
var ret = [];
for(var key in this.map) {
if(this.map.hasOwnProperty(key)) {
ret.push(this.map[key]);
}
}
return ret;
}
HashMap.prototype.size = function(activeCheck) {
//Active check is expensive.
if(typeof activeCheck !== 'undefined' && activeCheck) {
var count = 0;
for(var key in this.map) {
if(this.map.hasOwnProperty(key)) {
count++;
}
}
return count;
}
return this.listsize;
}
HashMap.prototype.save = function(){
return JSON.stringify(this.map);
}
HashMap.prototype.load = function(json) {
if(typeof json !== 'string') {
throw new Error("No valid input supplied. Only supply JSON Strings");
}
this.map = JSON.parse(json);
this.listsize = this.size(true);
}
var map = new HashMap();
console.log(
map.put('hello', true),
map.get('hello'),
map.put('hello',10),
map.put('world',20),
map.values(),
map.keySet(),
map.entrySet(),
map.containsValue('twoshoes'),
map.size()
);
var map2 = new HashMap();
map2.put('goody','twoshoes');
map2.putAll(map);
console.log(
map2.get('hello'),
map2.values(),
map2.keySet(),
map2.entrySet(),
map2.containsValue('twoshoes'),
map2.size()
);
var map3 = new HashMap();
map3.load(map2.save());
console.log(
map3.get('hello'),
map3.values(),
map3.keySet(),
map3.entrySet(),
map3.containsValue('twoshoes'),
map3.size()
);