I'm trying to make a binary search tree. If I start with an array my function makes a binary search tree out of that array (everything fine here). like for an array like let a = [2,4,5,3,9,7,3,8,5]; the tree looks like the picture. my problem is with the insert() function. If I start with an empty array and add a node to it, it works. However, when I add a second node, that second node won't be added to my tree, and my tree is shown as having only 1 node in it (the root node). Here the snippet:
const Node = (data, left = null, right = null) => {
return {data, left, right};
};
const Tree = array => {
const remDupsAndSort = array => {
const mergeSort = array => {
if(array.length <= 1) return array;
let leftArr = array.slice(0, array.length / 2);
let rightArr = array.slice(array.length / 2);
return merge(mergeSort(rightArr), mergeSort(leftArr))
};
const merge = (leftArr, rightArr) => {
let sorted = [];
while(leftArr.length && rightArr.length){
if(leftArr[0] < rightArr[0]){
sorted.push(leftArr.shift());
}else{
sorted.push(rightArr.shift());
}
};
return [...sorted, ...leftArr, ...rightArr]
};
return mergeSort([... new Set(array)])
};
array = remDupsAndSort(array);
const buildTree = (array, start, end) => {
if(start > end) return null;
let mid = Math.floor((start + end) / 2);
let node = Node(array[mid]);
node.left = buildTree(array, start, mid - 1);
node.right = buildTree(array, mid + 1, end);
return node;
};
const insert = value => {
if(!root) return root = Node(value);
current = root;
while(current){
if(value < current){
current = current.left
}else{
current = current.right
}
}
current = Node(value)
// if(!current){
// current = Node(value)
// // }else{
// if(value < current){
// current.left = insert(value, current.left)
// }else{
// current.right = insert(value, current.right)
// }
// }
return root
};
const prettyPrint = (node = root, prefix = '', isLeft = true) => {
if(node){
if (node.right !== null) {
prettyPrint(node.right, `${prefix}${isLeft ? '│ ' : ' '}`, false);
}
console.log(`${prefix}${isLeft ? '└── ' : '┌── '}${node.data}`);
if (node.left !== null) {
prettyPrint(node.left, `${prefix}${isLeft ? ' ' : '│ '}`, true);
}
}else{
console.log(node)
}
}
let root = buildTree(array, 0, array.length - 1);
return {root, prettyPrint, insert}
};
let a = [2,4,5,3,9,7,3,8,5];
let b = [];
let c = [1,2,4,5,6,7]
let f = Tree(a)
let d = Tree(b)
let e = Tree(c)
d.insert(4)
// d.insert(8) ---> doesn't work
// d.prettyPrint()
// f.insert(1) ---> doesn't work
f.prettyPrint()
// e.prettyPrint()
// console.log(d.root)
if I run d.prettyPrint() I'll get └── 4 just as expected. But if I run d.insert(8) after that 8 isn't added to the tree and the code returns └── 4 again. To make matters more confusing if I console.log(d.root) it returns null even though my prettyPrint function returns └── 4 as the root.
Clearly I expect the nodes be added to the tree. On one of my attempts I tried to write the code like this:
const insert = (value, current = root) => {
if(!current){
current = Node(value)
}else{
if(value < current){
current.left = insert(value, current.left)
}else{
current.right = insert(value, current.right)
}
}
return current
};
even though I assigned current = root the code returned null for d.insert(4)
There are these issues in your insert function:
current is implicitly defined as a global variable -- this wouldn't parse in strict mode. It should be declared as a local variable, using let
The value is compared with a node object instead of with the data of that node. So value < current should be changed to value < current.data
The assignment of the new node object to current -- after the loop -- will not mutate the tree. It merely assigns that object to that variable. Such assignment can never change the tree, just like current = current.right does not mutate the tree either. You need instead to assign the new node to either the left or right property of the parent of current. So this assignment should happen one step earlier.
Correction:
const insert = value => {
if(!root) return root = Node(value);
let current = root; // Define with `let`
while(current){
if(value < current.data){ // Compare with the data, not the node
// Mutate the parent node when inserting
if (!current.left) return current.left = Node(value);
current = current.left
}else{
if (!current.right) return current.right = Node(value);
current = current.right
}
}
};
Related
const LinkedListFactory = () => {
let _list = {head: null, tail: null};
const append = (value) => {
const node = NodeFactory(value);
if (!_list.head) {
_list.head = node;
_list.tail = node;
} else {
_list.tail.next = node;
_list.tail.next.prev = _list.tail;
_list.tail = _list.tail.next;
}
updateDisplay();
}
const prepend = (value) => {
const node = NodeFactory(value);
if (!_list.head) {
_list.head = node;
_list.tail = node;
} else {
_list.head = {value: node.value, next: _list.head, prev: null}
_list.head.next.prev = _list.head;
}
updateDisplay();
}
const size = () => {
if (!_list.head) return 0;
let count = 1;
let current = _list.head;
while (current.next) {
current = current.next;
count++;
}
return count;
}
const insertAt = (value, index) => {
if (index < 0) return 'invalid input: negative index';
if (index === 0) return prepend(value);
if (index >= size() || !_list.head) return append(value); //if index > list size OR list is empty, then append the value
let current = _list.head;
while (index > 0) {
current = current.next; //(*)
index--;
}
current = {value: value, next: current, prev: current.prev}; //(**)
current.prev.next = current; //(**)
current.next.prev = current; //(**)
updateDisplay();
}
return {
append, prepend, insertAt, _list //only for testing purposes
}
}
let list = LinkedListFactory();
list.append(1);
list.append(2);
list.prepend(0);
console.log(list._list);
Above is a method for a linked list that contains a head/tail property, and each node contains prev/value/next properties. The question lies in traversing the linked list, this is merely an example I wrote that works 100% correctly, I just don't know why it works.
On the lines marked with (**) the actual linked list (_list, a private object in a closure) is mutated. But the linked marked with (*) merely traverses the list but does not mutate _list at all. Why is that the case? I'm setting the current node equal to the following node, shouldn't that delete the current node altogether?
Again, this works as intended, but looking back at my old code I got more confused. I would greatly appreciate any clarification or direction in concepts to look into.
how to return to a binary tree multiple nodes in a array or
a set
simple solution
There are a few issues in your code:
It expects internal nodes to have exactly two children. You should make use of the method getNumberOfChildren
path shouldn't be a global variable, as that will pollute any further call of getPaths. I would suggest to use a generator function, so that the caller can take care of putting the results in their own array variable.
Obviously root.getValue() === pathsToFind is never going to be true as pathsToFind is an array, and the root's value is an integer. You can use includes, but that will bring a bad time complexity when the array is long. So I'd suggest to turn that array into a Set.
It wasn't explained in the question, but it seems like you don't want to include a path to a node, when that node is on a path to another node that needs to be included. In other words, when the array of values has pairs where one is the ancestor of the other, the ancestor can be ignored.
Here is how I would suggest to implement it (with generator):
function* generatePaths(root, targetSet) {
if (!root) return;
const val = root.getValue();
let foundPathsWithThisValue = false;
for (let child = 0; child < root.getNumberOfChildren(); child++) {
for (const path of generatePaths(root.getChild(child), targetSet)) {
path.push(val);
yield path;
foundPathsWithThisValue = true;
}
}
if (!foundPathsWithThisValue && targetSet.has(val)) {
yield [val];
}
}
function getPaths(root, targetValues) {
return Array.from(generatePaths(root, new Set(targetValues)), path =>
path.reverse().join("->")
);
}
// No change below
class Node {
constructor(value) {
this.value = value;
this.children = [];
}
addChild(node) {
this.children.push(node);
}
getValue() {
return this.value;
}
getChild(i) {
return this.children[i];
}
getNumberOfChildren() {
return this.children.length;
}
print(indent = 0) {
console.log("-".repeat(indent), this.value);
for (const child of this.children) {
child.print(indent + 4);
}
}
}
const root = new Node(0);
const leftRoot = new Node(10);
const rightRoot = new Node(500);
root.addChild(leftRoot);
root.addChild(rightRoot);
const leftChild = new Node(150);
const rightChild = new Node(250);
leftRoot.addChild(leftChild);
leftRoot.addChild(rightChild);
const leftleftChild = new Node(200);
leftChild.addChild(leftleftChild);
const rightrightChild = new Node(300);
rightChild.addChild(rightrightChild);
// will output [ '0 -> 10 -> 150 -> 200', '0 -> 500' ]
console.log(getPaths(root, [150, 200, 500]));
You are free to use your accessors instead of root.children. But I guess it is clearer like this, at least for the answer.
Be careful, your property 'root' has the same name as the global variable root
function getPaths(root, pathsToFind) {
let match = false;
if (root === null) return [];
if (pathsToFind.includes(root.getValue())) {
match = true;
}
const pathChildren = root.children.map(child => getPaths(child, pathsToFind))
.filter(child => child.length > 0)
.map(value => `${root.getValue()} -- ${value}`);
if (pathChildren.length > 0) {
return pathChildren
} else {
return match ? [`${root.getValue()}`] : []
}
}
I'm working on a problem where given an array of file paths I would like to print the file structure. For example with the given array ["/a/b/c", "a/a/a", "/a/b/d"], the ideal structure would look like :
a
b
c
d
a
a
But my structure ends up looking more like this:
a
b
c
a
a
a
b
From what I can gather this is being caused by my tree not recognizing when a node already exists in a tree. So it is adding the node "a" three times as opposed to recognizing that an "a" already exists and traversing into it.
let paths = ["/a/b/c", "a/a/a", "/a/b/d"]
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
}
addChild(element) {
this.children.push(element)
}
}
const head = new TreeNode('Head');
let cur = head;
paths.forEach(element => {
cur = head;
let filePath = element.split('/');
filePath.shift();
filePath.forEach(element => {
let newNode = new TreeNode(element);
if(!cur.children.includes(newNode)) {
cur.addChild(newNode);
cur = cur.children[cur.children.length - 1];
} else {
cur = cur.children.indexOf(newNode);
}
})
})
var spaceAppend = function(num) {
let i = 0;
let space = "";
while(i < num) {
space += " ";
i++;
}
return space;
}
var traverse = function(node, level = 0){
if(node === null)
return;
console.log(spaceAppend(level), node.value)
if(node.children) {
for(const n of node.children) {
traverse(n, level + 1);
}
}
}
traverse(head)
Is there an issue with my tree implementation?
Some issues:
.includes() is not the right way to find a matching value. Use .find() instead.
.indexOf() will return an index, so that is not the right value you want to assign to cur in the else block.
shift may throw away an essential part of the path when it does not start with /. You can ease the processing by using .match() instead of .split(), so that you get exactly the non-empty parts of the path.
Less of an issue:
There is no need to define cur outside of the outer loop.
JavaScript has a native function for something like spaceAppend. You can use .repeat().
new TreeNode(element) is also called when you actually don't need it. Only create a new node when you know there is no matching node.
You could replace the inner .forEach() loop with .reduce(), which gives a better scope-handling for the cur variable.
Here is your code with those remarks taken into account:
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
}
addChild(element) {
this.children.push(element);
}
}
let paths = ["/a/b/c", "a/a/a", "/a/b/d"];
const head = new TreeNode('Head');
paths.forEach(element => {
// Use .match() to only get non-empty elements
let filePath = element.match(/[^\/]+/g);
filePath.reduce((cur, element) => {
// Use .find() instead of .includes()
let node = cur.children.find(child => child.value === element);
// Only create the node when needed:
if (!node) {
node = new TreeNode(element);
cur.addChild(node);
}
// Walk down one step in the tree
return node; // ...becomes the value of `cur`
}, head); // Initial value of reduction
});
const traverse = function(node, level=0) {
if (node === null) return;
// Use .repeat():
console.log(" ".repeat(level), node.value);
if (node.children) {
for (const n of node.children) {
traverse(n, level + 1);
}
}
}
traverse(head);
Is the starter array meant to be ["/a/b/c", "/a/a/a", "/a/b/d"] ("/a/a/a" instead of ("a/a/a")?
I think the crux of the problem you're having is the line
if(!cur.children.includes(newNode)) { ... }
When a new node is created, even if it has the same value as a previous one, it will not result in equity when comparing the two TreeNode objects. You need to compare the value of the nodes, not the nodes themselves.
So an example with a simplified version of your node object:
class TreeNode {
constructor(value) {
this.value = value;
}
}
a1 = new TreeNode('a');
a2 = new TreeNode('a');
console.log("a1 == a2");
console.log(a1 == a2); // false
console.log("a1.value == a2.value");
console.log(a1.value == a2.value); // true
I adjusted the inner forEach loop with one that compares the values instead of the TreeNode objects
filePath.forEach(element => {
let newNode = new TreeNode(element);
let tempNode = null;
for (var i = 0; i < cur.children.length; i++) {
if (cur.children[i].value == newNode.value) {
tempNode = cur.children[i];
}
}
if (tempNode == null) {
cur.addChild(newNode);
cur = newNode;
} else {
cur = tempNode;
}
});
Full code snippet on codepen
Object equality in javascript isn't particularly nice to deal with see this other answer for more information
Here is a solution using lodash and object-treeify. While it's simpler code, there is obviously a trade-off introducing additional dependencies.
This solution works by first converting the paths into a tree structure and then visualizing it using object-treeify
// const lodash = require('lodash');
// const objectTreeify = require('object-treeify');
const myPaths = ['/a/b/c', 'a/a/a', '/a/b/d'];
const treeify = (paths) => objectTreeify(paths.reduce((p, c) => {
lodash.set(p, c.match(/[^/]+/g));
return p;
}, {}), {
spacerNoNeighbour: ' ',
spacerNeighbour: ' ',
keyNoNeighbour: '',
keyNeighbour: ''
});
console.log(treeify(myPaths));
/* =>
a
b
c
d
a
a
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/lodash#4.17.20"></script>
<script src="https://bundle.run/object-treeify#1.1.31"></script>
Disclaimer: I'm the author of object-treeify
I have written the following function in JavaScript to check if a singly Linked List is a palindrome. However, I'm failing 2 out of 10 tests, and I can't figure out why.
Here are the tests I'm falling.
l: [0, 1, 0]
l: [1, 1000000000, -1000000000, -1000000000, 1000000000, 1]
Both should return true for the palindrome, but my function is returning false.
Here's my code:
function isListPalindrome(l) {
let curr = l;
let arr = [];
if (l == null)
return true;
// push all elements of l into the stack.
// a stack in JS is an array.
while(curr != null){
arr.push(l.value);
// move ahead:
curr = curr.next;
}
let curr2 = l;
// Traverse the list again & check by popping from the stack:
while(curr2 != null){
// get the top most element on the stack:
let num = arr.shift();
// check if the node data isn't the same as the element popped:
if (curr2.value != num){
return false;
}
// move ahead:
curr2 = curr2.next;
}
return true;
}
Thank you!
Inside the first while loop you're pushing l.value but l is not being incremented so it's pushing the same value to arr.
You now have arr which is suppose to be l in reverse. In the second while loop, instead of using arr.shift() use arr.pop(). This will take the first element off the arr stack. Remember that a stack is first in, last out.
Notice also that when you're comparing the list front to back you'll reach a point of irrelevancy, the half way point. Once you know that half the values in the forward list are the same as the values in the reverse list you know the rest will pass the test.
Here's what it's suppose to look like. You should try to figure out how to do odds yourself.
function isListPalindrome(l) {
let curr = l;
let arr = [];
if (l == null)
return true;
// push all elements of l into the stack.
// a stack in JS is an array.
while(curr != null){
arr.push(curr.value);
// move ahead:
curr = curr.next;
}
let curr2 = l;
let length = arr.length;
// Traverse the list again & check by popping from the stack:
while(curr2 != null){
// get the top most element on the stack:
let lastNum = arr.pop();
// check if the node data isn't the same as the element popped:
if (curr2.value != lastNum){
return false;
}
// Half way point for evens
if (length / 2 === arr.length) {
return true;
}
// move ahead:
curr2 = curr2.next;
}
return true;
}
solving with pushing values to an array and then check if the array is palindromic will have S:O(N). with reversing the second half and then traversing will have S:O(1). T:O(N) is same for both:
var isPalindrome = function (head) {
let fast_pointer = head;
let slow_pointer = head;
// when fast_ppointer reaches to the tail, slow_pointer will be in the middle
while (fast_pointer && fast_pointer.next) {
fast_pointer = fast_pointer.next.next;
slow_pointer = slow_pointer.next;
}
// now, slow_pointer is in the middle and we reverse from slow_pointer till the head
let prev = null;
while (slow_pointer) {
// slow_pointer=slow_pointer.next how we iterate in linked lists.
// so make sure we keep a reference to the next iteration
temp = slow_pointer.next;
slow_pointer.next = prev;
prev = slow_pointer;
slow_pointer = temp;
}
let left = head;
let right = prev;
while (right) {
if (left.val !== right.val) {
return false;
}
left = left.next;
right = right.next;
}
return true;
};
var isPalindrome = function (head) {
let values = [];
while (head) {
values.push(head.val);
head = head.next;
}
let rev = [];
head.map((e) => {
rev.unshift(e);
});
if (values.every((val, index) => val === rev[index])) {
return true;
} else {
return false;
}
};
class Node {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
const is_palindromic_linked_list = function (head) {
let front = head;
const traverse = (node) => {
if (!node) return true;
//reverse the LL
const reverse = traverse(node.next);
//check value if they are equal
const valChecker = front.value == node.value;
front = front.next;
return reverse && valChecker;
}
return traverse(head)
};
head = new Node(2)
head.next = new Node(4)
head.next.next = new Node(6)
head.next.next.next = new Node(4)
head.next.next.next.next = new Node(2)
console.log(`Is palindrome: ${is_palindromic_linked_list(head)}`)
head.next.next.next.next.next = new Node(2)
console.log(`Is palindrome: ${is_palindromic_linked_list(head)}`)
I push all the elements of the list in an array and then I convert the array with join function to a string so that i can compare if the string is the same as the inverse using reverse function if it is then it is a palindrome
function isListPalindrome(l) {
if(l === null) return true;
let array =[];
let current = l;
while (current != null){
array.push(current.value);
current = current.next
}
if(array.join('')=== array.reverse().join('')) return true;
return false
}
I want to do a depth-first traversal for this binary tree:
1
/ \
4 5
/ \ \
4 4 5
Here's the node structure:
function TreeNode(data){
this.data = data
this.left = this.right = []
this.addLeft = function(node){
this.left.push(node)
}
this.addRight = function(node){
this.right.push(node)
}
}
The visit function (just to print out the node data):
function visit(node){
console.log(node.data)
}
The traverse function:
function traverse(node){
if(node === null) return
visit(node)
//visit left branch
for(node of node.left) traverse(node)
//visit right branch
for(node of node.right) traverse(node)
}
Add the binary tree structure:
let rootNode = new TreeNode(1)
let node_4A = new TreeNode(4)
let node_4B = new TreeNode(4)
let node_4C = new TreeNode(4)
let node_5A = new TreeNode(5)
let node_5B = new TreeNode(5)
//add root node branches
rootNode.addLeft(node_4A)
rootNode.addRight(node_5A)
node_4A.addLeft(node_4B)
node_4A.addRight(node_4C)
node_5A.addRight(node_5B)
The output:
1
4
4
4
5
5
5
So it correctly prints out the node data, but there's always an additional right-most node that got printed twice (i.e. the last 5). Do you have any idea why it happens?
I'm not too familiar with Javascript call stack, but could the reason be I'm running 2 for loops in a recursive function?
Thank you.
You take the same object reference for left and right.
this.left = this.right = []
You need independent arrays:
this.left = [];
this.right = [];
For taking the right node, take different names than node for iterating.
function traverse(node) {
if (!node) return; // you never have a value of null for a node
visit(node)
//visit left branch
for (let n of node.left) {
traverse(n);
}
//visit right branch
for (let n of node.right) {
traverse(n);
}
}
function TreeNode(data) {
this.data = data
this.left = [];
this.right = [];
this.addLeft = function (node) {
this.left.push(node)
}
this.addRight = function (node) {
this.right.push(node)
}
}
function visit(node) {
console.log(node.data)
}
function traverse(node) {
if (!node) return; // you never have a value of null for a node
visit(node)
for (let n of node.left) {
traverse(n);
}
for (let n of node.right) {
traverse(n);
}
}
let rootNode = new TreeNode(1)
let node_4A = new TreeNode(4)
let node_4B = new TreeNode(4)
let node_4C = new TreeNode(4)
let node_5A = new TreeNode(5)
let node_5B = new TreeNode(5)
//add root node branches
rootNode.addLeft(node_4A)
rootNode.addRight(node_5A)
node_4A.addLeft(node_4B)
node_4A.addRight(node_4C)
node_5A.addRight(node_5B)
traverse(rootNode);
DFS (depth first search) is usually easy to implement using recursion. refer to this function definition in js language -
const recFnc = (currNode) => {
if (currNode !== null) {
if (currNode.lNode !== null) {
recFnc(currNode.lNode, lPath);
}
if (currNode.rNode !== null) {
recFnc(currNode.rNode, rPath);
}
}
};
recFnc(rootNode);
Refer to this class I have created - https://www.npmjs.com/package/#dsinjs/binary-tree
and Refer to this function documentation which deals with path calculations and more - https://dsinjs.github.io/binary-tree/#find
You do this:
this.left = this.right = []
So the left leaf is actually the same as the right one. You want:
this.left = [];
this.right = [];
Just for fun: This would actually be a good usecase for generators:
TreeNode.prototype.leftFirst = function*() {
yield this.data;
for(const child of this.left.concat(this.right))
yield* child.leftFirst();
};
So you can do:
for(const node of tree.leftFirst())
console.log(node);