I found this algorithm from Data structures and algorithms in javascript. For the insertion method, there are two references to the root (current and parent). My question is, why can't i change both current and parent to this.root? They both point to this.root. The code does not work properly when I do that however
'use strict';
var BST = function() {
this.root = null;
//embed _Node inside BST so it comes with when BST is created
this._Node = function(data, left, right) {
this.data = data;
this.count = 1;
this.left = left;
this.right = right;
};
this._Node.prototype.show = function() {
return this.data;
};
this._Node.prototype.showCount = function() {
return this.count;
}
};
BST.prototype.insert = function(data) {
//use this data for a new node
var n = new this._Node(data, null, null);
//if root is null, put this new node and its data into root
if (this.root === null) {
this.root = n;
}
else {
//find a child position for this node
var current = this.root;
var parent;
while (true) {
parent = current;
if (data < current.data) {
current = current.left;
if (current === null) {
parent.left = n;
break;
}
}
else {
current = current.right;
if (current === null) {
parent.right = n;
break;
}
}
}
}
};
var nums = new BST();
nums.insert(23);
nums.insert(45);
nums.insert(16);
nums.insert(37);
nums.insert(3);
nums.insert(99);
nums.insert(22);
current does not refer to this.root throughout the entire algorithm.
It is initialized as this.root, but then it is quickly reassigned to either current = current.left; or current = current.right;. From that moment on current is no longer this.root. It is either this.root.left or this.root.right.
On the next iteration of the while loop it will get reassigned again, but it will never be this.root again, since it is alway being reassigned to a child node of current.
parent is similar, being this.root only on the first iteration. On each subsequent iteration it is reassigned by parent = current; and since current is no longerthis.root,parentwon't bethis.root` either.
parent is used just to hold reference of the previous node, you create new node n and then find it's position in the tree, once current becomes null, you have found the target position for node n, you need to assign it as child (left or right) to the parent node
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();
class Node {
constructor(val) {
this.val = val;
this.next = null;
}
}
class Stack {
// construstor
constructor() {
this.first = null;
this.last = null;
this.size = 0;
}
// use push
push(val) {
// newNode
let newNode = new Node(val)
// If length is 0
if (this.size == 0) this.first = this.last = newNode;
// if not
else {
let current = this.first;
// reset the first
this.first = newNode;
console.log(">>><<<<<<<<<>>>>><", typeof current, current, this.first, typeof this.first)
}
this.size++;
}
}
let s = new Stack;
s.push(1);
s.push(2);
I am writing code for stack implemention by looking into a tutorial.
I was confused at this step
console.log(">>><<<<<<<<<>>>>><",typeof current,current,this.first,typeof this.first)
I am getting typeof current as object according to my understanding object are reference types in my previous step I initialized
this.first = newNode;
As reference types this.first and current must be same but they are showing different values why (first has different value and current has different value)?
They're showing different values because you're reassigning them to different values.
When you do
current = this.first;
they're references to the same object. But then you do
this.first = newNode;
Now they're not references to the same object any more. current contains a reference to the previous contents of this.first, while this.first now contains a reference to newNode.
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 used Javascript to write a circular linked list and to detect and remove the loop.It is working fine untill the part of loop detection. How ever it is failing to remove the loopnode. More specifically: the removeLoop function of this code doesnot work.
Here is my code:
function Node(element){
this.element = element;
this.next = null;
}
//circular linked list class
function LList() {
this.head = new Node("head");
this.head.next = this.head;
this.find = find;
this.insert = insert;
this.display = display;
}
function find(item){
var curr = this.head;
while(curr.element != item){
curr = curr.next;
}
return curr;
}
//inserting items into linked list
function insert(newElem, after){
var newNode = new Node(newElem);
var curr = this.find(after);
newNode.next = curr.next;
curr.next = newNode;
}
function display() {
var currNode = this.head;
while ((currNode.next !== null) &&
(currNode.next.element !== "head")) {
console.log(currNode.next.element);
currNode = currNode.next;
}
}
function findPrevious(item){
var curr = this.head;
while(curr.next !== null && curr.next.element !== item){
curr =curr.next;
}
return curr;
}
//creating a linkedlist object
var furniture = new LList();
furniture.insert("chair","head");
furniture.insert("table", "chair");
furniture.insert("couch", "table");
furniture.insert("stool","couch");
//furniture.display();
//detecting if a linked list is circular
function detectALoop(list){
var slow = list.head;
var fast = list.head;
while(slow && fast && fast.next){
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
removeLoop (slow, list);
return 1;
}
}
return 0;
}
//This part of the code doesnot work
function removeLoop(loopNode, list)
{
var ptr1 = loopNode;
var ptr2 = loopNode;
var looplen = 1,i;
// count the number of nodes in loop
while(ptr1.next != ptr2)
{
ptr1 = ptr1.next;
looplen++;
}
console.log(looplen)
ptr1 = list.head;
ptr2 = list.head;
for(i=0; i <= looplen; i++)
{
ptr2 = ptr2.next;
}
while(ptr2.next != ptr1.next)
{
ptr1 = ptr1.next;
ptr2 = ptr2.next;
}
ptr2.next = null; // breaking the loop
}
console.log(detectALoop(furniture))
furniture.display();
You are making this a lot more complicated than it needs to be if the loop has to be back onto the first element.
function breakLoop(list) {
var head = list.head, tail = head, len = 1;
while (tail.next != head) {
len++;
tail = tail.next;
}
tail.next = null;
console.log(len.toString());
}
Now if you may need to handle any arbitrary loop, I still have no idea what you need 3 loops for. Use an ES6 Set; most browsers now support this, I believe. I'm going to go ahead and return the length instead of logging it.
function breakLoopAnywhere(list) {
var seen = new Set, node = list.head;
while (!seen.has(node.next)) {
seen.add(node);
node = node.next;
}
node.next = null;
return seen.size;
}
If you don't have sets, you can hack it with an array, replacing has with indexOf and add with push.
If you feel you must have the ability to detect a loop vs a non-looping list without breaking it:
// takes a node, returns the node
// that points backwards on its next
function getLoopNode(node) {
var seen = new Set;
do {
seen.add(node);
} while (!seen.has(node.next) && node = node.next)
return node;
}
function detectLoop(node) {
return getLoopNode(node) != null;
}
function breakLoop(node) {
node = getLoopNode(node);
if (node) node.next = null;
}
Your detectALoop is less complicated, but it's wrong. The only loop this will detect is if node 2i loops back onto node i. But the list could be 3 elements long looping onto the start; it could be lots of numbers that aren't 2i and i. Since there are probably a lot of numbers, way too many to try them all, you can't fix this strategy. There is no clever way to find cycles in a graph that is any faster or more intuitive than the one I wrote above. As far as I know.
This variable is messed up...
var looplen = 1,i;
It looks like you want it to be a 1.
Your removeLoop code is wrong, it never terminates:
let's assume this list:
A -> B -> C -> A
with loop length 3.
You correctly find the loop length, 3, you then set ptr1 and ptr2 to the head of the list, and then call .next on ptr2 for the length of the loop + 1 times (because of <=).
// for i = 0; i <= 3
A.next -> B // i = 0
B.next -> C // i = 1
C.next -> A // i = 2
A.next -> B // i = 33
So in the end you have ptr2 = B and ptr1 = A, i.e. ptr2 === ptr1.next!
One is the next of the other, and in the while loop you advance both until one is equal to the other, but they will never be, because they always be one the next of the other!
If you change the <= to just < it works, but the second while loop is actually useless.