I am trying to build this simple Javascript Binary search tree. I have simply created the addItem method for the tree, but no item seems to get added to the tree. I have divided the addItem method into several other methods to ensure that the tree reference is passed properly without any errors. I think the problem is occurring in the addNode recursive calls.
Below the is the given code:
class Node{
constructor(value){
this.value=value;
this.left=null;
this.right=null;
}
show(){
console.log(this.value);
}
}
class BST{
constructor(){
this.root=null;
}
addNode(node, item){
if(node==null){
node=new Node(item);
}
else if(item<=node.value){
this.addNode(node.left, item);
}
else {
this.addNode(node.right, item);
}
}
addFunc(tree, item){
this.addNode(tree.root, item);
}
addItem(item){
this.addFunc(this, item);
}
}
let bst = new BST();
bst.addItem(5);
bst.addItem(43);
bst.addNode(12);
console.log(bst); // shows BST{root:null}
One of the problem is in function addNode() at if(node==null){node=new Node(item);}
node is passed as a parameter, which means when this.addNode(tree.root, item); is called
node.a = 5 changes value of tree.root.a to 5
node = 5 just changes the value of this node parameter to 5 but not the actual argument that is tree.root value is not assigned to 5.
More info
First, you've not assigned anything as the root of your tree. Your intent was there with this.root. Next, your logic has you recursing a method rather than recursing your tree.
While you call the addNode() method for each value, you always test the root of your tree for > and < values. So this code will only ever overwrite a single .left or .right value. So recursing your function internally doesn't really do what you expect.
The solution is to recurse the tree looking for the correct slot for the value that is passed into the function.
I've re-written your code a bit and taken some liberties with the API. For instance, creating a new BST will require the starting value to be passed into the constructor (this just helps simplify the code a bit). Next, you'll notice no more recursive functions. The recursive calls are replaced with a while loop that finds the appropriate node to add the new node.
This code actually builds the tree where as your attempt only builds a single level.
Edit refactored into search function
class Node {
constructor(value) {
this.value = value;
this.left = {};
this.right = {};
}
show() {
console.log(this.value);
}
}
class BST {
constructor(value = 0) {
this.root = new Node(value);
}
addNode(item) {
Object.assign(this.search(item), new Node(item));
}
findLeftChild(item) {
return this.search(item).left.value;
}
search(item) {
let found = false;
let foundNode = this.root;
while (!found) {
if (foundNode.value === item) {
break;
} else if (foundNode.value > item) {
foundNode = foundNode.left;
found = foundNode.value === undefined;
} else if (foundNode.value < item) {
foundNode = foundNode.right;
found = foundNode.value === undefined;
}
}
return foundNode;
}
addItem(item) {
this.addNode(item);
}
}
let bst = new BST(5);
bst.addItem(43);
bst.addItem(12);
bst.addItem(6);
bst.addItem(66);
bst.addItem(22);
bst.addItem(4);
bst.addItem(3);
bst.addItem(2);
bst.addItem(1);
console.log("Found: 17 ", bst.search(17).value !== undefined);
console.log("find value less than 12: " + bst.findLeftChild(12));
console.log(JSON.stringify(bst, null, 2)); // shows BST{root:null}
Related
Code line in question:
callbackFn ? callbackFn(currentNode) : levelOrderList.push(currentNode.value);
I am having trouble of a way to think of this in psuedo-code terms since 'callbackFn' is used like a function but not defined like a function.
I know this code works and have ran it myself. I have also solved this without using the callbackFn, but I would really like to understand why this works.
My guess for psuedo cod would be:
if callbackFn exists (not null or undefined), then return callbackFn(currentNode).
else push currentNode.value to the levelOrderList.
Full code for context:
function levelOrder(callbackFn) {
const queue = [this.root];
const levelOrderList = [];
while (queue.length > 0) {
const currentNode = queue.shift();
callbackFn ? callbackFn(currentNode) : levelOrderList.push(currentNode.value);
const enqueueList = [
currentNode?.leftChild,
currentNode?.rightChild
].filter((value) => value);
queue.push(...enqueueList);
}
if (levelOrderList.length > 0) return levelOrderList;
}
Your guess for pseudo code is correct.
The author of that could should better have used an if...else structure like your pseudo code does. The conditional operator (? :) is used here as an unnecessary short-cut. Normally you would use the conditional operator to use the value that it evaluates to, like x = condition ? a : b;. But here that value is ignored. There is really no good reason to avoid if...else here.
The author added support for a callback mechanism as an alternative to returning an array. This doesn't look like best practice to me either. For two reasons:
This "polymorphism" can be confusing for the user of such an API. It is easier to understand when the two functionalities are offered by two different functions, one that returns the result in an array, another that calls the callback. The caller will choose the function based on how they want to deal with the traversed nodes.
A callback mechanism is rather "old style". It makes more sense to turn this function into a generator function. The caller can then easily decide what to do with the nodes that the returned iterator yields: collect those nodes in an array or just process them one by one.
This is how that generator function would look like:
class Node {
constructor(value) {
this.value = value;
this.leftChild = this.rightChild = null;
}
}
class Tree {
constructor(...values) {
this.root = null;
for (let value of values) this.add(value);
}
add(value) {
function addTo(node) {
if (!node) return new Node(value);
if (value < node.value) {
node.leftChild = addTo(node.leftChild);
} else {
node.rightChild = addTo(node.rightChild);
}
return node;
}
this.root = addTo(this.root);
}
*levelOrder() {
if (!this.root) return;
const queue = [this.root];
while (queue.length > 0) {
const currentNode = queue.shift();
yield currentNode.value;
if (currentNode.leftChild) queue.push(currentNode.leftChild);
if (currentNode.rightChild) queue.push(currentNode.rightChild);
}
}
}
// Demo
const tree = new Tree(4, 6, 7, 2, 1, 5, 3);
// Several ways to use the levelOrder generator function:
console.log(...tree.levelOrder());
console.log(Array.from(tree.levelOrder()));
for (let value of tree.levelOrder()) console.log(value);
I was given this as a solution to delete all duplicate numbers in a Linkded list. But I don't understand how to pass in the linked list?
var deleteDuplicates = function(head) {
// sets current node to be head of list
let current = head
// runs until we are at the end of the list
while (current !== null && current.next !== null) {
// checks to see if the current value and the next value are the same
if (current.val === current.next.val){
// skips over the duplicate and the next value becomes 2x next
current.next = current.next.next
// current value and the next value are not the same
} else {
// moves to the next node on the list to run through the while again
current = current.next
}
}
// returns the linked list with no duplicates
return head
};
A example to pass a List to function getListSize(). Is this what you want?
class ListNode {
constructor(data) {
this.data = data
this.next = null
}
}
class LinkedList {
constructor(head = null) {
this.head = head
}
}
let node1 = new ListNode(2)
let node2 = new ListNode(5)
node1.next = node2
let list = new LinkedList(node1)
function getListSize(list){
let count = 0;
let node = list.head;
while (node) {
count++;
node = node.next
}
return count;
}
console.log(getListSize(list));
You created a class but don't instantiate it, to do so call the constructor of the class using the new keyword:
const instance = new LinkedList();
then you need to pass the head property of that instance to the function, currently you are sending an undefined value (head doesn't exist outside the deleteDuplicates function's scope).
deleteDuplicates(instance.head);
i also noticed that your function deleteDuplicates won't work for what you want to do, here is a function that should do what you are trying to achieve:
function deleteDuplicates(list) {
if (!Array.isArray(list)) {
console.error(`Given argument is not an Array: ${list}`);
return;
};
return list.map((element, index, array)=>{
if (array.indexOf(element)!==index) return undefined;
return element;
})
.filter((element)=>(element!==undefined));
}
for more informations about what i said:
how classes work: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class
understand scope: https://developer.mozilla.org/en-US/docs/Glossary/Scope
I see Binary Tree implementations like this:
var insert = function(value, root) {
if (!root) {
// Create a new root.
root = { val: value };
}
else {
var current = root;
while (current) {
if (value < current.val) {
if (!current.left) {
// Insert left child.
current.left = { val: value };
break;
}
else {
current = current.left;
}
}
else if (value > current.val) {
if (!current.right) {
// Insert right child.
current.right = { val: value };
break;
}
else {
current = current.right;
}
}
else {
// This value already exists. Ignore it.
break;
}
}
}
return root;
}
var exists = function(value, root) {
var result = false;
var current = root;
while (current) {
if (value < current.val) {
current = current.left;
}
else if (value > current.val) {
current = current.right;
}
else {
result = true;
break;
}
}
return result;
}
var traversePre = function(head, callback) {
// Preorder traversal.
if (head) {
if (callback) {
callback(head.val);
}
traversePre(head.left, callback);
traversePre(head.right, callback);
}
}
var traversePost = function(head, callback) {
// Postorder traversal.
if (head) {
traversePost(head.left, callback);
traversePost(head.right, callback);
if (callback) {
callback(head.val);
}
}
}
var traverseIn = function(head, callback) {
// Inorder traversal.
if (head) {
traverseIn(head.left, callback);
if (callback) {
callback(head.val);
}
traverseIn(head.right, callback);
}
}
var traverseInIterative = function(head, callback) {
// Inorder traversal (iterative style).
var current = head;
var history = [];
// Move down to the left-most smallest value, saving all nodes on the way.
while (current) {
history.push(current);
current = current.left;
}
current = history.pop();
while (current) {
if (callback) {
callback(current.val);
}
// Move to the right, and then go down to the left-most smallest value again.
current = current.right;
while (current) {
history.push(current);
current = current.left;
}
current = history.pop();
}
}
var root = insert(10);
insert(5, root);
insert(6, root);
insert(3, root);
insert(20, root);
Particularly, traverseInIterative looks pretty good to me. But I'm wondering if there's really a need to have insert and exists, and likewise to have search or delete. I get that (like in these implementations) that they are implemented differently, but wondering if you could implement a generic matching function that solves all of it in one swoop that would be at the same time as ideal as it gets, performance-wise.
One way to design a generic method to do all the operations would be -
genericMethod(value, root, command)
Here command parameter would receive a string specifying insert or delete or search. And based on the command parameter you can tweak the inner implementation to support all of the operations.
Now let's come to the matter of performance and design perspective. I don't think having a method like this would be ideal. Having a generic method like this would cause you more problem than you can think.
Now after reviewing your code - there are a number of things that can be improved which will give you a better experience in my opinion like -
For insertion/deletion - you need to check if the value already exists or not. So just call exists() method rather writing same codes in those method.
This type of generic behavior ensures that you are not writing same codes again and again also SRP(Single Responsibility Principle), so your code is perfectly compartmentalized and more easily readable.
Here is my class Sample.
A Sample instance can:
have a number of tags such as Tag1, Tag2, etc.
be queried with method isTagged to find out whether it has been tagged or not tagged (ie. !Tag1)
function Sample(){
// [..]
this.tags = [];
// [..]
}
Sample.prototype.tag = function(tags){
// [..]
this.tags[tags] = true;
// [..]
};
// if an array is passed, isTagged will return true at the first match ie. not all need to match, just one
Sample.prototype.isTagged = function(tag){
if(tag){
if(Array.isArray(tag)){
let tLength = tag.length;
while(tLength--){
if(isTaggedSingleNoChecks(this, tag[tLength])){
return true;
}
}
return false;
}
else{
return isTaggedSingleNoChecks(this, tag);
}
}
return false;
};
function isTaggedSingleNoChecks(sample, tag){
const isNegated = tag.charAt(0) == "!";
if(isNegated){
tag = tag.replace(/^[!]/, "");
return sample.tags[tag]!==true;
}
else{
return sample.tags[tag]===true;
}
}
// showing usage
var sample = new Sample();
sample.tag('Tag1');
sample.tag('Tag2');
console.log(sample.isTagged('Tag1'));
console.log(sample.isTagged('Tag3'));
console.log(sample.isTagged('!Tag2'));
This all works great however my application recursively queries isTagged millions of times on thousands of instances of Sample, and my profiling is showing this to be a performance bottleneck.
Any suggestions on how to improve performance?
Before you start optimizing this, how about simplifying the code first and getting rid of the most obvious oddities (objects instead of Sets, useless regexes etc)
class Sample {
constructor() {
this.tags = new Set();
}
tag(...tags) {
for (let t of tags)
this.tags.add(t);
}
isTagged(...tags) {
return tags.some(t =>
(t[0] === '!')
? !this.tags.has(t.slice(1))
: this.tags.has(t)
)
}
}
If this is still too slow, then you have to resort to a global object-tag inverted index, for example:
class SetMap extends Map {
get(key) {
if (!this.has(key))
this.set(key, new Set)
return super.get(key)
}
}
let tagIndex = new SetMap()
class Sample {
tag(...tags) {
for (let t of tags) {
tagIndex.get(t).add(this)
}
}
isTagged(...tags) {
return tags.some(t => tagIndex.get(t).has(this))
}
}
Of course, some more work will be involved for untagging (tag removal) and, especially, proper serialization.
The index won't immediately speed up isTagged per se, but will greatly optimize queries "find objects that are tagged by X and/or Y".
My solution works well if the starting node is passed to the function correctly. I want to know if my solution is good and efficient. I should be able to return true if the cycle exists via function to which first node is passed as parameter. I would like to know if my solution is efficient especially for an interview setting. My comments in the code are self explanatory. Im using a variable track to traverse through the list and checking for a null or head as the next. If i encounter any of them traversal ends and then individually i check for null or head condition and based on that i return the appropriate boolean value.
function SLLNode(elem) {
this.value=elem;
this.next=null;
}
var hasCycle=function(node){
var track=node;
//traverse thru list till next node is either null or back to first node
while(track.next!==null && track.next!==this.head){
track=track.next;
}
if(track.next === null){ //if next node null then no cycle
return false;
}
if(track.next===this.head){ //if next node head then there is cycle
return true;
}
}
var my_node1=new SLLNode(3);
var my_node2=new SLLNode(5);
var my_node3=new SLLNode(19);
//assigning head
var head=my_node1;
//connecting linked list
my_node1.next=my_node2;
my_node2.next=my_node3;
my_node3.next=my_node1; //cycle
console.log("Has cycle?: "+hasCycle(my_node1)); //outputs true as expected
var node1=new SLLNode(3);
var node2=new SLLNode(5);
var node3=new SLLNode(19);
//assigning head
var head1=node1;
node1.next=node2;
node2.next=node3;
console.log("Has cycle?: "+hasCycle(node1)); //outputs false as expected
JSON.stringify() can be used to detect cyclic linked lists. CircularDetector returns true if the linked list is cyclic.
function CircularDetector (head) {
try {
JSON.stringify(head);
return false;
} catch (e) {
return true;
}
}
You can read more on cycle detection at https://en.wikipedia.org/wiki/Cycle_detection but the main takeaway is that if you move one pointer twice as fast as another pointer then a loop would be identifiable as the fast pointer will eventually catch up with the other. Here's a possible solution in js.
function hasCycle(head) {
var slow, fast;
if(!head || !head.next) return false;
slow = head;
fast = head;
if(head.next === head) return true;
while(fast.next.next) {
slow = slow.next;
fast = fast.next.next;
if(slow === fast) return true;
}
return false;
}
Not a very efficient solution as I am using map but if you don't want to use two pointers this solution is easy to understand
// Preparation code:
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
function hasCycle(head) {
let node = head;
let map={};
while(node){
if(map[node.value]){
//return node or true
return {"Found":node}
}
else{
map[node.value] = true;
}
node = node.next;
}
return "Not found";
}
const nodeA = new Node('A');
const nodeB = nodeA.next = new Node('B');
const nodeC = nodeB.next = new Node('C');
const nodeD = nodeC.next = new Node('D');
const nodeE = nodeD.next = new Node('E');
console.log(hasCycle(nodeA)); // => null
nodeE.next = nodeB;
console.log(hasCycle(nodeA))