I'm trying to implement a self balancing tree (beginning with BST first ).
So i have multiple keys with same value. So i have decided to make primary and secondary comparison to place a value in a node.
My node structure looks like ->
var Node = function (value) {
this.fine = value.fine ; // primary comparison
this.index = value.index ; // secondary comparison
this.left = null ;
this.right = null ;
};
fine is int value and index is array index.
my BST insert looks like this->
function insert(root, data) {
if(root == null) {
root = new Node(data)
}
// base case for secondary comparison .
if(root.fine === data.fine ) {
if(root.index > data.index ) {
root.left = new Node(data) ;
} else {
root.right = new Node(data)
}
}
if(root.fine > data.fine ) {
root.left = insert(root.left, data) ;
} else if(root.fine < data.fine ) {
root.right = insert(root.right , data) ;
return root;
}
but i am getting MAXIMUM STACK CALL. i think the base case is not right! What should be the base case of this problem?
Related
I am testing Zag-zag rotation with a right-skewed tree. Before that, I had tested Zig-zig, Zig-zag rotation with a left-skewed tree and OK.
Here is my code:
// An AVL tree node
class Node {
constructor(key) {
this.key = key;
this.left = this.right = null;
}
}
function rightRotate(root) {
const rootLeft = root.left; // saving the reference
root.left = rootLeft.right;
rootLeft.right = root;
return rootLeft; // new root
}
function leftRotate(root) {
const rootRight = root.right;
root.right = rootRight.left;
rootRight.left = root;
return rootRight;
}
// This function modifies the tree and returns the new root
// - If has key, Brings the key at root
// - Else, brings the last accessed item at root.
function splay(root, key) {
// Base cases
if (root === null || root.key === key) return root; // bring last accessed element or founded element as root of left-left (sub)tree
// Grandparent start here
// Key lies in Left
if (key < root.key) {
// Key is not in tree
if (root.left === null) return root; // If not founded, Bring last accessed element root of left (sub)tree
// Parent start here
// Zig-Zig (Left Left)
if (key < root.left.key) {
root.left.left = splay(root.left.left, key);
// Do first rotation for root, brind child as parent of subtree
root = rightRotate(root);
}
// Zig-Zag (Left Right)
else if (key > root.left.key) {
root.left.right = splay(root.left.right, key);
// Do first rotation for root.left, brind child as parent of subtree
if (root.left.right != null) root.left = leftRotate(root.left);
}
// 1 cong 2 viec
// 1. Do second rotation, bring child as grandparent of subtree
// 2. Bring parent (level 2) as root of tree when last recursive splay() finish
return rightRotate(root);
// Parent end
}
// Key lies in Right
else {
if (root.right === null) return root;
// Parent start here
// Zag-Zag (Right Right)
if (key > root.right.key) {
root.right.right = splay(root.right.right, key);
root = leftRotate(root);
}
// Zag-Zig (Right Left)
else if (key < root.right.key) {
root.right.left = splay(root.right.left, key);
if (root.right.left != null) root.right = rightRotate(root.right);
}
return leftRotate(root);
// End parent
}
// Grandparent end
}
// The search function for Splay tree.
// This function returns the new root of Splay Tree.
// If key is present in tree then, it is moved to root.
// else, last accessed element is moved to root
function search(root, key) {
return splay(root, key);
}
// A utility function to print
// preorder traversal of the tree.
// The function also prints height of every node
function preOrder(root) {
if (root != null) {
console.log(root.key);
preOrder(root.left);
preOrder(root.right);
}
}
// Right-skewed tree for Zag-zag and Zag-zig test
// Using root2 to console.log in other function like splay, search (because they have argument name "root")
let root2 = new Node(10);
root2.right = new Node(15);
root2.right.right = new Node(16);
root2.right.right.right = new Node(20);
root2.right.right.right.right = new Node(21);
root2.right.right.right.right.right = new Node(22);
root2 = splay(root2, 20);
console.log(root2)
Assume we search for node 20. The rotation should happen as follow:
Zag-zag rotation: Replace node 15 with 20.
Zag rotation: Replace node 10 with 20
However, my code result is:
Demo of image used this
Both your algorithm and the imaged algorithm perform double rotations correctly, every time taking a pair of edges on the path to the target node, and applying a double rotation on that pair.
However, when the path to the target node has an odd length, then there is one edge that will undergo a single rotation. The choice of this single edge is different:
your algorithm will "split" the path in pairs starting from the root node, treating the remaining single edge at the end of the path separately.
the imaged algorithm will "split" the path in pairs starting from the target node, treating the remaining single edge at the start of the path (at the root) separately.
Here is your code aligned with that second strategy:
function splay(root, key) {
function splaySub(root) {
if (!root) throw new RangeError; // Value not found in tree
if (root.key === key) return root;
let side = key < root.key ? "left" : "right";
root[side] = splaySub(root[side]);
// Check if odd: then caller to deal with rotation
if (root[side].key === key) return root;
// Apply a double rotation, top-down:
root = key < root.key ? rightRotate(root) : leftRotate(root);
return key < root.key ? rightRotate(root) : leftRotate(root);
}
try {
root = splaySub(root);
return !root || root.key === key
// Path had even length: all OK
? root
// Odd length: Perform a final, single rotation
: key < root.key ? rightRotate(root) : leftRotate(root);
} catch(e) {
if (!(e instanceof RangeError)) throw e;
return root; // Not found: return original tree
}
}
When the searched value is not in the tree, this code will trigger an Exception, so to exit quickly out of recursion without any update to the tree. In this case that approach makes the code a bit cleaner (fewer checks for null values...).
Addendum
As in comments you explained that the splay function should also bring a node to the top when it doesn't find the value, I provide here the updated code. I took the opportunity to turn the code into more OOP style, so you'll find here some of your functions turned into methods:
class Node {
constructor(key, left=null, right=null) {
this.key = key;
this.left = left;
this.right = right;
}
* inOrder(depth=0) {
if (this.right) yield * this.right.inOrder(depth+1);
yield [depth, this.key];
if (this.left) yield * this.left.inOrder(depth+1);
}
_rotate(toRight) {
const [side, otherSide] = toRight ? ["right", "left"] : ["left", "right"];
const orig = this[otherSide];
this[otherSide] = orig[side];
orig[side] = this;
return orig;
}
rightRotate() {
return this._rotate(true);
}
leftRotate() {
return this._rotate(false);
}
insert(key) {
const side = key < this.key ? "left" : "right";
if (this[side]) return this[side].insert(key);
this[side] = new Node(key);
}
}
class SplayTree {
constructor(...values) {
this.root = null;
for (const value of values) this.insert(value);
}
insert(value) {
if (this.root) this.root.insert(value);
else this.root = new Node(value);
}
splay(key) {
if (!this.root) return;
function splaySub(root) {
if (root.key === key) return root;
const side = key < root.key ? "left" : "right";
// Not found? Then do as if we looked for current node's key
if (!root[side]) {
key = root.key; // Modifies the outer-scoped variable
return root;
}
root[side] = splaySub(root[side]);
// Check if odd: then caller to deal with rotation
if (root[side].key === key) return root;
// Apply a double rotation, top-down:
root = root._rotate(key < root.key);
return root._rotate(key < root.key);
}
this.root = splaySub(this.root);
if (this.root.key !== key) this.root = this.root._rotate(key < this.root.key);
}
search(key) {
this.splay(key);
}
* inOrder() {
if (this.root) yield * this.root.inOrder();
}
toString() {
return Array.from(tree.inOrder(), ([depth, key]) => " ".repeat(depth) + key).join("\n");
}
}
const tree = new SplayTree(10, 15, 16, 20, 21, 22);
console.log("Initial tree:");
console.log(tree.toString());
tree.search(19); // Not found, so it splays 20.
console.log("Final tree:");
console.log(tree.toString());
I'm implementing an algorithm (Kruslkal) that needs to merge two or more binary trees in javascript, for example:
The following tree:
4
5 6
Can be merged into the following tree:
2
1 3
... resulting:
2
1 3
4
5 6
I put the binary tree data structure code, but when I did a test in a function that merges the trees, called 'merge', nothing happens. The first tree is not merged in the second tree, and if I try to use console.log in the function 'merge', the following message appears: "Uncaught TypeError: tree is null".
Can someone help me with this?
function binarytree()
{
this.root = null;
this.add = function(value)
{
var node = {
value : value,
left : null,
right : null
};
var current;
if (this.root == null) this.root = node;
else
{
current = this.root;
while (1)
{
if (value < current.value)
{
if (current.left == null)
{
current.left = node;
break;
}
else current = current.left;
}
else if (value > current.value)
{
if (current.right == null)
{
current.right = node;
break;
}
else current = current.right;
}
else break;
}
}
}
this.search = function(value)
{
var found = false,
current = this.root;
while (!found && current)
{
if (value < current.value) current = current.left;
else if (value > current.value) current = current.right;
else found = true;
}
return found;
}
this.print = function(no)
{
if (no)
{
this.print(no.left);
this.print(no.right);
console.log(no.value);
}
}
}
var tree = new binarytree();
var tree2 = new binarytree();
function merge(tree, tree2)
{
//console.log("tree.value " + tree.value);
if (tree == null) tree = tree2.root;
else if (tree.value < tree2.value) this.merge(tree.left, tree2);
else this.merge(tree.right, tree2);
}
tree.add(1);
tree.add(2);
tree.add(3);
console.log("First tree:");
tree.print(tree.root);
tree2.add(7);
tree2.add(8);
tree2.add(9);
console.log("Second tree:");
tree2.print(tree2.root);
merge(tree.root,tree2.root);
console.log("Merged trees:");
tree.print(tree.root);
Looking at your code, it is clear that you are dealing with not just any binary trees, but binary search trees. These trees ensure that the value of a node is never less than the value of its left child, and never greater than the value of its right child.
Your example is therefore not correctly pictured. This is not a binary search tree:
4
5 6
It would be correct if it were:
5
4 6
Moreover, your code is not creating these trees. Instead it is creating these trees:
1 and 7
2 8
3 9
If you want to create more balanced trees, you should change the order of the insertions. For example:
tree.add(2); // First!
tree.add(1);
tree.add(3);
This will create:
2
1 3
The Error
...if I try to use console.log in the function 'merge', the following message appears: "Uncaught TypeError: tree is null".
This is expected, as you make recursive calls like with this.merge(tree.left, tree2), and tree.left can be null. Even in the next statement you check this case with if (tree == null), so it is normal you get this error.
But your code shows that you think that an assignment to tree with tree = tree2.root; will somehow perform the attachment of tree2 inside tree. But this is just an assignment to a variable, not to a left or right property of a node in the tree, so nothing is happening to the tree with this assignment. Remember that JavaScript passes values, so when you pass tree.left as argument to a function, you can be sure that tree.left will still reference the same object once the function has returned.
In short, you should make the assignment one step earlier, when you arrive at a leaf, not when you arrive at a null. Something like this:
function merge(tree, tree2) {
if (tree2.value < tree.value) {
if (tree.left) {
this.merge(tree.left, tree2);
} else {
tree.left = tree2;
}
} else {
if (tree.right) {
this.merge(tree.right, tree2);
} else {
tree.right = tree2;
}
}
}
The deeper problem
However, while the above will perform a simple attachment of one tree to another, it assumes that the range of values of the first tree does not overlap with the range of values in the second tree. If there is an overlap, this procedure will not produce a binary search tree. A merge that maintains the BST property, will need to distribute the nodes of the second tree at different places in the first tree.
One way to do that is to take every value of the second tree and call add(value) on the first tree. This will work fine. It has a time complexity of O(nlogm), where m is the size of the first tree, and n of the second tree.
If the tree sizes are comparable, you'll get a better time complexity when you walk through the first tree in one sweep, inserting new nodes as you pass by the right insertion spot. This will have a time complexity of O(m+n).
Implementation
I would change a lot to your code:
Use class syntax... and define methods on the prototype, not on each instance
Define an iterator to visit nodes in inorder sequence
Avoid the code repetition in add and search.
Define a class for constructing node objects instead of using an object literal for that
... several other improvements
Here it is:
class Node { // Create a class for this
constructor(value, left=null, right=null) {
this.value = value;
this.left = left;
this.right = right;
}
* inorder() {
if (this.left) yield * this.left.inorder();
yield this.value;
if (this.right) yield * this.right.inorder();
}
}
class BinaryTree { // Use class syntax and PascalCase
constructor() {
this.root = null;
}
add(value) {
let [location, side] = this.locate(value);
if (side) location[side] = new Node(value); // Use constructor instead of plain object literal;
}
locate(value) { // Returns where node is or should be inserted
if (!this.root) return [this, "root"];
let current = this.root;
while (true) {
if (value < current.value) {
if (!current.left) return [current, "left"];
current = current.left;
} else if (value > current.value) {
if (!current.right) return [current, "right"];
current = current.right;
}
else return [current, ""];
}
}
search(value) {
return !this.locate(value)[1];
}
print() { // Use iterator to get the values
for (let value of this.inorder()) console.log(value);
}
* inorder(node) {
if (this.root) yield * this.root.inorder();
}
merge(otherTree) {
let values = otherTree.inorder();
let nextValue = values.next().value;
function recur(node, max) {
while (nextValue !== undefined && nextValue < max) {
if (nextValue < node.value) {
if (!node.left) {
node.left = new Node(nextValue);
nextValue = values.next().value;
}
recur(node.left, node.value);
} else if (nextValue > node.value) {
if (!node.right) {
node.right = new Node(nextValue);
nextValue = values.next().value;
}
recur(node.right, max);
} else {
nextValue = values.next().value;
}
}
}
recur(this.root, Infinity);
}
}
var tree = new BinaryTree();
var tree2 = new BinaryTree();
tree.add(2);
tree.add(4);
tree.add(6);
console.log("First tree:");
tree.print();
tree2.add(1);
tree2.add(3);
tree2.add(5);
console.log("Second tree:");
tree2.print();
tree.merge(tree2);
console.log("Merged trees:");
tree.print();
I'm trying to determine if a Binary Search Tree is balanced. I'm not too clear on how to store the depth of the childnodes of the left and right branch. I'm trying to return true if the right branch is greater than the left branch by a length of a max of 1 and vice versa.
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* #param {TreeNode} root
* #return {boolean}
*/
var isBalanced = function(root) {
var checkChild = function(root) {
if (this.left) {
var left = 1;
this.left.checkChild(root);
if (this.right) {
left += 1;
this.right.checkChild(root);
}
if (this.right) {
var right = 1;
this.right.checkChild(root);
if (this.left) {
right += 1;
this.right.checkChild(root);
}
}
}
if (left - right > 1 || right - left > 1) {
return false;
}
return true;
};
};
I was thinking of creating a var to increment every-time a node is traversed for both the right and left branches starting from the head. But I'm realizing that this will compare the total number of nodes from the left branch to the total number of nodes on the right branch, which won't work.
At each check why are you sending the head again like
why root again?
this.left.checkChild(root)
Instead, if you want to find the depth, your implementation should look something like this:
function treeDepth(tree)
{
if (tree === null)
return 0;
else
{
/* compute the depth of each subtree */
let leftDepth = treeDepth(tree.left);
let rightDepth = treeDepth(tree.right);
/* use the larger one */
if (leftDepth > rightDepth)
return(leftDepth+1);
else return(rightDepth+1);
}
}
At first find max depth of the root, then find min depth of the root. It is easy using dfs. Next step is to check difference of these depths.
The code will look like this:
class Node {
constructor(value) {
this.value = value
this.left = null
this.right = null
}
}
var isBalanced = function(root) {
var foo = function(root, fun) {
if (root === null) return 0
l = foo(root.left, fun)
r = foo(root.right, fun)
return fun(l, r);
}
return foo(root, Math.max) - foo(root, Math.min) < 2
}
let tree = new Node(1)
tree.left = new Node(2)
tree.left.left = new Node(3)
tree.left.left.left = new Node(4)
tree.right = new Node(5)
tree.right.left = new Node(6)
document.write(isBalanced(tree))
If you want to use checkChild as a method, you should define it as such, not as a variable. I would also suggest to not return a boolean, but the real difference in depth between left and right subtree. This will give more information to the caller, who can still treat that value as a boolean if so desired (falsy means balanced, truthy means tilted).
Here is how your implementation could look:
class TreeNode {
constructor(val) {
this.val = val;
}
add(node) {
const dir = node.val < this.val ? "left" : "right";
if (this[dir]) {
this[dir].add(node);
} else {
this[dir] = node;
}
}
height() {
return Math.max(
this.left ? this.left.height() + 1 : 0,
this.right ? this.right.height() + 1 : 0
);
}
tilt() {
return (this.left ? this.left.height() + 1 : 0)
- (this.right ? this.right.height() + 1 : 0);
}
static from(...data) {
if (!data.length) return;
const root = new TreeNode(data[0]);
for (let v of data.slice(1)) {
root.add(new TreeNode(v));
}
return root;
}
}
const root = TreeNode.from(13, 4, 9, 16);
console.log(root);
console.log('Tilt at root = ', root.tilt());
.as-console-wrapper { max-height: 100% !important; top: 0; }
So I'm trying to create a Binary Search Tree using Pseudoclassical inheritance. It accepts an array, sorts it, uses the middle value as the starting point, and inserts the remaining values from the array into the BST. I guess I'm trying my best to utilize functional programming (correct me if I'm wrong please) by using reuseable methods and also because a BST insert method needs to be recursive.
I've pointed out where the code errors out. I believe it takes 3 as the initial value, I also believe 1 (the next value in the array) successfully gets inserted, but I believe the number 2 is where the error occurs when it says that "TypeError: this.left.insert is not a function". Can anyone point out what I'm doing wrong? Why won't the insert method call itself for this.left?
var NoDuplicatesBST = function(array) {
var tempArr = arguments[0].sort(function(a, b) {
return a-b;
});
var middle = Math.floor(((tempArr.length - 1) / 2));
var sliced = tempArr.splice(middle, 1);
this.createBST(sliced[0]);
// now insert the rest of tempArr into the BST
for (var i = 0; i < tempArr.length; i++) {
this.insert(tempArr[i]);
}
};
NoDuplicatesBST.prototype.createBST = function(number) {
this.value = number;
this.left = null;
this.right = null;
};
NoDuplicatesBST.prototype.insert = function(number) {
if (number < this.value) {
if (this.left === null) {
this.left = new this.createBST(number);
} else {
// ------------CODE BELOW DOES NOT WORK!, LINED 77 ALSO PROBABLY. TypeError: this.left.insert is not a function----------------------
this.left.insert(number);
}
} else if (number > this.value) {
if (this.right === null) {
this.right = new this.createBST(number);
} else {
this.right.insert(number);
}
} else {
// Do nothing
}
};
var testBST = new NoDuplicatesBST([2,3,4,5,1]);
console.log("The testBST:", testBST);
That's not written in functional way, take a look and try to go thru this tutorial to learn more about functional programming in JS: http://reactivex.io/learnrx/
And to the original question why you see the "TypeError: this.left.insert is not a function". Check my comments in your code:
var NoDuplicatesBST = function(arr) {
var middle, left = [], center, right = [];
if (!Array.isArray(arr) || arr.length == 0) {
return this;
}
if (arr.length == 1) {
center = arr[0];
} else {
middle = Math.floor((arr.length / 2));
center = arr[middle];
left = arr.slice(0, middle);
right = arr.slice(middle + 1, arr.length);
console.log('left:', left);
console.log('middle:', center);
console.log('right:', right);
}
this.createBST(center);
// now insert left and right parts to BST
if (left.length > 0) {
this.insert(left);
}
if (right.length > 0) {
this.insert(right);
}
};
NoDuplicatesBST.prototype.createBST = function(number) {
this.value = number;
this.left = null;
this.right = null;
};
NoDuplicatesBST.prototype.insert = function(arr) {
if (arr.length > 0) {
//array is sorted and we took the middle element, so we can compare just the first element
if (arr[0] < this.value) {
/** Here you use createBST as a constructor, it creates a new element,
with left and right values, but you break the prototypal inheritance chain,
that's why you don't have access to the insert function */
// this.left = new this.createBST(number);
// it's better to pass the part of the array to build the tree further
this.left = new NoDuplicatesBST(arr);
} else {
this.right = new NoDuplicatesBST(arr); //the same as above
}
}
};
var arr = [2, 3, 4, 5, 1];
var tempArr = arr.reduce(function (noDuplicatesArr, current) { //remove duplicates
if (noDuplicatesArr.indexOf(current) === -1) {
noDuplicatesArr.push(current);
}
return noDuplicatesArr;
}, []).sort(function(a, b) {
return a - b;
});
var testBST = new NoDuplicatesBST(tempArr);
console.log("The testBST:", testBST);
For prototypal chain inheritance check: https://developer.mozilla.org/en/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
BTW. I changed your code to accept arrays instead of numbers, now that will build a BST
I have a C# script like below:
public List<MazePath> BreakIntoConnectedPaths()
{
List<MazeVertex> remainVertices = new List<MazeVertex>(vertices);
List<MazePath> paths = new List<MazePath>();
while (remainVertices.Count > 0)
{
MazePath path = new MazePath();
path.entrancePosition = entrancePosition;
path.exitPosition = exitPosition;
VisitCell(path, remainVertices.First(), null, remainVertices);
paths.Add(path);
//Store the coordinate for entrance and exit
}
return paths;
}
void VisitCell(MazePath path, MazeVertex ver, MazeVertex parent, List<MazeVertex> remainVertices)
{
remainVertices.Remove(ver);
path.Add(ver);
for (int i = 0; i < ver.connectVertices.Count; i++)
{
MazeVertex ver2 = ver.connectVertices[i];
if (ver2 != parent)
{
VisitCell(path, ver2, ver, remainVertices);
}
}
}
I want to convert it to javascript as below
BreakIntoConnectedPaths = function() {
var remainVertices = _.cloneDeep(this.vertices);
var paths = [];
while (remainVertices.length > 0) {
var path = new Path();
path.entrancePos = this.entrancePos;
path.exitPos = this.exitPos;
this.VisitCell(path, remainVertices[0], null, remainVertices);
paths.push(path);
// Store the coordinate for entrance and exit
}
return paths;
}
VisitCell = function(path, vertex, parentVertex, remainVertices) {
_.remove(remainVertices, function(v) {
return v.x === vertex.x && v.z === vertex.z;
});
path.Add(vertex);
for (var i = 0; i < vertex.connectVertices.length; i++) {
var connectedVertex = vertex.connectVertices[i];
// if (parentVertex && (connectedVertex.x !== parentVertex.x || connectedVertex.z !== parentVertex.z)) {
if(parentVertex && _.isEqual(connectedVertex, parentVertex)) {
VisitCell(path, connectedVertex, vertex, remainVertices);
}
}
}
The _ symbol here is lodash sign.
After I convert to javascript code, the behavior of these functions is difference with the C# one. With the same vertices data, the paths array had returned with difference size.
Thanks you for reading and pls help me if you see my mistake here.
In the C# version, your VisitCell function has a condition that says if(ver2 != parent), but in the JS version you check that they are equal instead of not equal.
Also, that condition would never pass any way because in your first call to that function you pass in null for the parent, but in that condition you check that the parent is "truthy".
Lodash's isEqual can handle null values, so I'm not sure why you're checking if the parent is truthy there. Perhaps you meant to do this?
if(!_.isEqual(connectedVertex, parentVertex)) {
There are several ways to improve your JavaScript code. When transpiling code, it is better to not copy/paste and fix, but to rewrite using the target language instead.
I would prefer to have this written, for example:
var vertices;
var entrancePos;
var exitPos;
function Path(entrancePos, exitPos){
this.entrancePos = entrancePos;
this.exitPos = exitPos;
this.Add = function() {
// your Add() code here
}
}
function breakIntoConnectedPaths() {
var remainingVertices = _.cloneDeep(vertices);
var paths = [];
while (remainVertices.length) {
var path = new Path(entrancePos, exitPos);
visitCell(path, remainingVertices.shift());
// Store the coordinate for entrance and exit
paths.push(path);
}
return paths;
}
function visitCell(path, vertex, parentVertex) {
path.Add(vertex);
for (var i = 0; i < vertex.connectVertices.length; i++) {
var connectedVertex = vertex.connectVertices[i];
if(_.isEqual(connectedVertex, parentVertex)) {
visitCell(path, connectedVertex, vertex);
}
}
}
Keep in mind that the variables vertices, entrancePos, exitPos and Path are not available to me on your C# code, so I only declare them on JavaScript. Implement them as you may.
Does that fix it, by the way?