How can I unite two trees in javascript? - javascript

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();

Related

Splay Tree implementation: Search function - Zag zag rotation wrong

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());

Implementing BST TREE DUPLICATES KEYS - JS

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?

How to implement deque data structure in javascript?

I'm Learning data structure with javascript
and my focus now on how to implement deque?
Edite: from comments below I get useful directions on how to implement deque based array. Is there a direction how to implement deque based object using class ?
I get understand some points like I need :
addFront()
removeFront()
peekFront()
addBack()
removeBack()
peekBack()
but I'm confused about some points :
how many pointers I need ?
at least I know from queue I need two(head-tail) pointer but not sure if I need more in deque
which data type in javascript convenient in this case as a base? I saw some tutors in youtube talking about circular array for example which unknown for me in JS.
edite2:
I was following a book called: learning javascript data structures and algorithms 3rd edition
in chapter5 of this book the author started to implement Deque based on object only and some variables
but I didn't understand how he did that because the code encrypted but I can still reach to his files from and test his approach github repository
I can say that #trincot answer very close of book author approach
but when I compare the results I get this [1 = author - 2 = #trincot] :
according to the book index taking about linked list comes in chapter6 so I didn't expect his solution will be based on something he didn't mentioned before
plz if I miss any point I will be grateful to tell me it ... thanks
As stated in comments, JavaScript has native support for deque operations via its Array class/prototype: push, pop, shift, unshift.
If you still want to write your own implementation, then you can go for a doubly linked list, where you just need two "pointers". It should be said that in JavaScript we don't really speak of pointers, but of objects. Variables or properties that get an object as value, are in fact references in JavaScript.
Alternatively, you can go for a circular array. Since in JavaScript standard Arrays are not guaranteed to be consecutive arrays as for example is the case in C, you don't really need to use an Array instance for that. A plain object (or Map) will do.
So here are two possible implementations:
Doubly Linked List
class Deque {
constructor() {
this.front = this.back = undefined;
}
addFront(value) {
if (!this.front) this.front = this.back = { value };
else this.front = this.front.next = { value, prev: this.front };
}
removeFront() {
let value = this.peekFront();
if (this.front === this.back) this.front = this.back = undefined;
else (this.front = this.front.prev).next = undefined;
return value;
}
peekFront() {
return this.front && this.front.value;
}
addBack(value) {
if (!this.front) this.front = this.back = { value };
else this.back = this.back.prev = { value, next: this.back };
}
removeBack() {
let value = this.peekBack();
if (this.front === this.back) this.front = this.back = undefined;
else (this.back = this.back.next).back = undefined;
return value;
}
peekBack() {
return this.back && this.back.value;
}
}
// demo
let deque = new Deque;
console.log(deque.peekFront()); // undefined
deque.addFront(1);
console.log(deque.peekBack()); // 1
deque.addFront(2);
console.log(deque.removeBack()); // 1
deque.addFront(3);
deque.addFront(4);
console.log(deque.peekBack()); // 2
deque.addBack(5);
deque.addBack(6);
console.log(deque.peekBack()); // 6
console.log(deque.removeFront()); // 4
console.log(deque.removeFront()); // 3
console.log(deque.removeFront()); // 2
console.log(deque.removeFront()); // 5
console.log(deque.removeFront()); // 6
console.log(deque.removeFront()); // undefined
Circular "Array"
class Deque {
constructor() {
this.data = {}; // Or Array, but that really does not add anything useful
this.front = 0;
this.back = 1;
this.size = 0;
}
addFront(value) {
if (this.size >= Number.MAX_SAFE_INTEGER) throw "Deque capacity overflow";
this.size++;
this.front = (this.front + 1) % Number.MAX_SAFE_INTEGER;
this.data[this.front] = value;
}
removeFront() {
if (!this.size) return;
let value = this.peekFront();
this.size--;
delete this.data[this.front];
this.front = (this.front || Number.MAX_SAFE_INTEGER) - 1;
return value;
}
peekFront() {
if (this.size) return this.data[this.front];
}
addBack(value) {
if (this.size >= Number.MAX_SAFE_INTEGER) throw "Deque capacity overflow";
this.size++;
this.back = (this.back || Number.MAX_SAFE_INTEGER) - 1;
this.data[this.back] = value;
}
removeBack() {
if (!this.size) return;
let value = this.peekBack();
this.size--;
delete this.data[this.back];
this.back = (this.back + 1) % Number.MAX_SAFE_INTEGER;
return value;
}
peekBack() {
if (this.size) return this.data[this.back];
}
}
// demo
let deque = new Deque;
console.log(deque.peekFront()); // undefined
deque.addFront(1);
console.log(deque.peekBack()); // 1
deque.addFront(2);
console.log(deque.removeBack()); // 1
deque.addFront(3);
deque.addFront(4);
console.log(deque.peekBack()); // 2
deque.addBack(5);
deque.addBack(6);
console.log(deque.peekBack()); // 6
console.log(deque.removeFront()); // 4
console.log(deque.removeFront()); // 3
console.log(deque.removeFront()); // 2
console.log(deque.removeFront()); // 5
console.log(deque.removeFront()); // 6
console.log(deque.removeFront()); // undefined
Methods will return undefined, when an attempt is made to retrieve a value from an empty deque.
Dequeue implementation in a simple way:
const dequeue = [];
// push element from rear end
dequeue.push(3); // [3]
dequeue.push(8); // [3, 8]
// push element from front end
dequeue.unshift(5); // [5, 3, 8]
dequeue.unshift(11); // [11, 5, 3, 8]
// pop element from rear end
dequeue.pop(); // [11, 5, 3]
// pop element from front end
dequeue.shift(); // [5, 3]
The tricot's Doubly Linked List, but refactored and typed:
type DequeNode<T> = {
value: T;
prev?: DequeNode<T>;
next?: DequeNode<T>;
};
class Deque<T = any> {
front?: DequeNode<T>;
back?: DequeNode<T>;
constructor(...initialValues: T[]) {
initialValues.forEach(initialValue => {
this.addBack(initialValue);
});
}
addFront(value: T) {
if (!this.front) {
this.front = this.back = { value };
return;
}
this.front = this.front.next = { value, prev: this.front };
}
removeFront() {
if (!this.front) {
return;
}
const value = this.peekFront();
if (this.front === this.back) {
this.front = undefined;
this.back = undefined;
return value;
}
(this.front = this.front.prev!).next = undefined;
return value;
}
peekFront() {
return this.front?.value;
}
addBack(value: T) {
if (!this.front) {
this.front = this.back = { value };
return;
}
this.back = this.back!.prev = { value, next: this.back };
}
removeBack() {
if (!this.back) {
return;
}
const value = this.peekBack();
if (this.front === this.back) {
this.front = undefined;
this.back = undefined;
return value;
}
(this.back = this.back.next!).prev = undefined;
return value;
}
peekBack() {
return this.back?.value;
}
}
As with any other attempt at understanding new stuff, it is helpful to have a comparative approach.
JS arrays are deques because you can modify the front out-of-the-box. You don't get this in Python where the out-of-the-box list structure supports only modification in the back (calling it append and pop). If you need to start adding and removing front items you need to explicitly add support for deques (by adding from collections import deque at the top of the module ) and create the object with a dedicated constructor (d = deque([1,2,3]).Only then you can perform popleft and appendleft operations (called unshift and shift in JS)
Again, none of this is required in JS, the implementation of JS array supports this OOTB. To get a feeling for terminology across the language landscape see the Wikipedia's table at
https://en.wikipedia.org/wiki/Double-ended_queue#Operations

Remove nodes from Linked List based on a value

I am working through a Hackerrank problem and I am trying to remove all nodes that are greater than a particular value.
This is their basic implementation
const SinglyLinkedListNode = class{
constructor(value) {
this.value = value;
this.next = null;
}
};
const SinglyLinkedList = class {
constructor() {
this.head = null;
this.tail = null;
}
insertNode(value) {
const node = new SinglyLinkedListNode(value);
if(!this.head) {
this.head = node;
} else {
this.tail.next = node;
}
this.tail = node;
}
};
My function to removeNodes is as follows...
SinglyLinkedList.prototype.removeNodes = function(listHead, x){
let currentNode = listHead;
while(currentNode !== null && currentNode.next !== null) {
let next = currentNode.next;
while (next !== null && next.value > x) {
next = next.next
}
currentNode.next = next
if(currentNode.next === null) {
break;
}
}
return currentNode
}
The parameters are: listhead - reference to the root node, and x - the integer value to filter the Linked List
So for example, the LL is 1-> 2 -> 4 -> 3 -> 5 and I need to remove all nodes greater than x (3) and maintain the integrity of the LL order.
The result should be 1 -> 2 -> 3.
I am getting confused on why I keep getting this.tail.value = 5 instead of
this.tail.value = 3, this.tail.next = null.
this is a REPL
Cause the tail has to be rewritten explicitly as it keeps a reference to an unlinked node otherwise. Before running the function the list looks like:
list: head tail
1 -> 2 -> 3 -> 4 -> 5
and afterwards the tail still points to 5 although that was unlinked:
list: head tail
1 -> 2 -> 3 5
To resolve this, just rewrite the tail at the end of your function:
return this.tail = currentNode;
Also you have to actually traverse the list, so add a
currentNode = currentNode.next;
at the end of the outer while.

Issues with a recursive function when trying to create a BST using Pseudoclassical Inheritance

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

Categories