I'm building a non ordered binary tree that looks like this
1
/ \
30 2
\ / \
31 4 3
/ \
34 32
.... and so on
My implementation has been created as follows
function BinarySearchTree() {
this._root = null;
}
BinarySearchTree.prototype = {
//restore constructor
constructor: BinarySearchTree,
add: function(value,whereToAdd,nodeToAdd){
//create a new item object, place data in
var node = {
value: value,
left: null,
right: null
},
//used to traverse the structure
current;
//special case: no items in the tree yet
if (this._root === null || whereToAdd === null || whereToAdd === undefined){
this._root = node;
} else {
current = nodeToAdd;
while(true){
//if the new value is less than this node's value, go left
if (whereToAdd === "no"){
//if there's no left, then the new node belongs there
if (current.left === null){
current.left = node;
break;
} else {
current = current.left;
}
//if the new value is greater than this node's value, go right
} else if (whereToAdd === "yes"){
//if there's no right, then the new node belongs there
if (current.right === null){
current.right = node;
break;
} else {
current = current.right;
}
//if the new value is equal to the current one, just ignore
} else {
break;
}
}
}
},
traverse: function(process){
//helper function
function inOrder(node){
if (node){
//traverse the left subtree
if (node.left !== null){
inOrder(node.left);
}
//call the process method on this node
process.call(this, node);
//traverse the right subtree
if (node.right !== null){
inOrder(node.right);
}
}
}
//start with the root
inOrder(this._root);
},
contains: function(value){
var found = false,
current = this._root;
//make sure there's a node to search
while(!found && current){
//if the value is less than the current node's, go left
if (value < current.value){
current = current.left;
//if the value is greater than the current node's, go right
} else if (value > current.value){
current = current.right;
//values are equal, found it!
} else {
found = true;
}
}
//only proceed if the node was found
return current;
},
size: function(){
var length = 0;
this.traverse(function(node){
length++;
});
return length;
},
toArray: function(){
var result = [];
this.traverse(function(node){
result.push(node.value);
});
return result;
},
toString: function(){
return this.toArray().toString();
},
};
function ArmTheTree(Tree){
Tree.add(1,null,null);
Tree.add(30,"no",Tree.contains(1));
Tree.add(31,"yes",Tree.contains(30));
Tree.add(32,"yes",Tree.contains(31));
Tree.add(33,"no",Tree.contains(32));
Tree.add(34,"no",Tree.contains(31));
Tree.add(35,"yes",Tree.contains(34));
Tree.add(36,"yes",Tree.contains(35));
Tree.add(2,"yes",Tree.contains(1));
Tree.add(4,"no",Tree.contains(2));
Tree.add(23,"no",Tree.contains(4));
Tree.add(24,"yes",Tree.contains(23));
Tree.add(25,"no",Tree.contains(23));
Tree.add(26,"yes",Tree.contains(25));
Tree.add(27,"no",Tree.contains(25));
Tree.add(28,"no",Tree.contains(27));
Tree.add(29,"yes",Tree.contains(27));
Tree.add(3,"yes",Tree.contains(2));
Tree.add(5,"yes",Tree.contains(3));
Tree.add(6,"no",Tree.contains(3));
Tree.add(7,"yes",Tree.contains(6));
Tree.add(8,"no",Tree.contains(6));
Tree.add(9,"yes",Tree.contains(8));
Tree.add(17,"no",Tree.contains(9));
Tree.add(19,"yes",Tree.contains(17));
Tree.add(20,"no",Tree.contains(17));
Tree.add(21,"yes",Tree.contains(20));
Tree.add(10,"yes",Tree.contains(9));
Tree.add(11,"yes",Tree.contains(10));
Tree.add(12,"no",Tree.contains(10));
Tree.add(15,"yes",Tree.contains(12));
Tree.add(13,"no",Tree.contains(12));
Tree.add(14,"yes",Tree.contains(13));
Tree.add(16,"no",Tree.contains(13));
};
Tree = new BinarySearchTree();
ArmTheTree(Tree);
});
How can I use my implementation of the transverse function to search for a node within the binary tree? My intention is to use this tree for a true or false game.
You can use a recursive approach with an auxiliary function _contains, which looks for the value in a node in the tree and its subtrees:
...,
_contains: function(node, value) {
if (node === null) return null;
if (node.value == value) return node;
// search in left subtree
var n = this._contains(node.left, value);
if (n) return n;
// search in right subtree
return this._contains(node.right, value);
},
contains: function(value) {
return this._contains(this._root, value);
},
...
The function returns null if the node isn't found.
If you make the function add return the newly added node, you could save yourself a lot of tree-walking when building the tree and you could probably do without the contains function. Instead of:
Tree.add(1, null, null);
Tree.add(30, "no", Tree.contains(1));
Tree.add(31, "yes", Tree.contains(30));
you'd get:
var n = {};
n[1] = Tree.add(1, null, null);
n[30] = Tree.add(30, "no", n[1]);
n[31] = Tree.add(31, "yes", n[30]);
Related
I'm trying to change the select2.julll.js file from node_modules Angular 6 project.
So far I found some ways to do that tho, nothing has worked for me, would you fave any suggestions of how I can replace wrappedMatcher with startMatcher in select2 file?
S2.define('select2/compat/matcher',[
'jquery'
], function ($) {
function oldMatcher (matcher) {
function wrappedMatcher (params, data) {
var match = $.extend(true, {}, data);
if (params.term == null || $.trim(params.term) === '') {
return match;
}
if (data.children) {
for (var c = data.children.length - 1; c >= 0; c--) {
var child = data.children[c];
// Check if the child object matches
// The old matcher returned a boolean true or false
var doesMatch = matcher(params.term, child.text, child);
// If the child didn't match, pop it off
if (!doesMatch) {
match.children.splice(c, 1);
}
}
if (match.children.length > 0) {
return match;
}
}
if (matcher(params.term, data.text, data)) {
return match;
}
return null;
}
return wrappedMatcher;
}
return oldMatcher;
});
Adding the following code to my select2.directives.ts has solved my problem
if (data.text.toString().toLowerCase().indexOf(params.term) > -1 &&
!!data.text.toString().toLowerCase().startsWith(params.term.toString().toLowerCase())) {
var modifiedData = $.extend({}, data, true);
// modifiedData.text += ' (matched)';
// You can return modified objects from here
// This includes matching the `children` how you want in nested data sets
return modifiedData;
}
Below is the implementation of a BST with an insertion function for it. currently, the code wouldn't work; It would just spit out Tree { root: null }
When i tried to debug it, it seems that it successfully adds the new Node to the correct spot, but once it returns from the function, all that data is lost and it ends up not inserting anything.
here is the code:
class Node {
constructor(value) {
this.value = value
this.left = null;
this.right = null;
}
}
class Tree {
constructor() {
this.root = null
}
insert(value) {
const insertHelper = (value, node) => {
if (node === null) {
node = new Node(value)
return null
} else if (node.value === node.value) {
console.log("Value exists.")
return null;
} else if (node.value < node.value) {
return this.insertHelper(node, node.right)
} else {
return this.insertHelper(node, node.left)
}
}
return insertHelper(value, this.root)
}
}
var tree = new Tree;
tree.insert(10)
tree.insert(5)
console.log(tree);
Several issues:
this.root is never modified. Function arguments are passed by value, so if you pass this.root as argument, and the function assigns a new value to the corresponding parameter variable node, this will not affect this.root. The solution is to let the helper function return the new value of the node that is passed as argument, so you can assign it back to the root (or other node).
At several places you compare node.value with node.value. That is a mistake. The comparison should involve value.
The recursive calls pass node as first argument, while the function expects the value as first argument.
Here is the corrected code:
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class Tree {
constructor() {
this.root = null;
}
insert(value) {
const insertHelper = (value, node) => {
if (node === null) {
node = new Node(value);
} else if (node.value === value) {
console.log("Value exists.");
} else if (node.value < value) {
node.right = insertHelper(value, node.right);
} else {
node.left = insertHelper(value, node.left);
}
return node;
}
this.root = insertHelper(value, this.root);
}
}
var tree = new Tree;
tree.insert(10);
tree.insert(5);
console.log(tree);
NB: use semi-colons explicitly. Relying on the automatic semi-colon insertion is asking for trouble. One day it will hit you.
Problem: Is there a way I can use TreeListNode in Tree.insert(element)? The constraint is that singleLinkedList.addToTail(element) takes an element and returns a ListNode, which does not have 'children' and 'parent' attributes, and if I construct TreeListNode(element) first then I would not be able to use singleLinkedList.addToTail(element).
Context: I am learning Data Structures in javascript. Having already implemented LinkedList and Queues, I am now trying to use them as a library to implement an n-ary tree with Depth-first and Breadth-first traversals.Through this, I am also trying to learn javascript best practices. Whoever can suggest further tips on how to further improve my code would have my undying gratitude. Is the pattern I have followed (with regard to using a Position, ListNode and TreeListNode objects) alright? Have I correctly followed "composition over inheritance"?
Here is my Tree implementation:
const Queue = require('./queue.js')
const LinkedListLibrary = require('./linkedListTest.js');
function TreeNode(element){
LinkedListLibrary.ListNode.call(this, element);
this.parent = null;
this.children = new LinkedListLibrary.SingleLinkedList()
}
TreeNode.prototype = Object.create(LinkedListLibrary.ListNode.prototype)
function Tree(rootElement){
this._root = new TreeNode(rootElement);
this._size = 1;
};
Tree.prototype = {
isEmpty: function() {
return (this._size == 0)
//Returns true if the tree is empty. True only initially
},
size: function() {
return this._size
// Returns the number of nodes in the tree.
},
getRoot: function(){
//console.log("Entered getRoot function");
return this._root
},
traverseDF: function(callback){
// This method traverses a tree with depth-first search.
(function recurse(currentNode){ //Step 2
for (var i = 0; i < currentNode.children.length; i++){
recurse(currentNode.children.getNode(i)) //Step 3. When currentNode = leaf, length = 0
};
//console.log("Traversed the children of: " + JSON.stringify(currentNode.getElement()));
callback(currentNode) //Step 4
})(this.getRoot()) //Step 1. Immediate Invoke Recurse with the root node as argument
},
traverseBF: function(callback){
var bfQueue = new Queue();
bfQueue.enqueue(this.getRoot());
while(!bfQueue.isEmpty()){
console.log("Next element to dequeue:" + bfQueue._head.data.getElement())
var currentTreeNode = bfQueue.dequeue();
console.log("currentTreeNode contains " + currentTreeNode.getElement())
callback(currentTreeNode)
for (var i = 0; i < currentTreeNode.children.length; i++){
var temp = currentTreeNode.children.getNode(i);
console.log("enqueing: " + JSON.stringify(temp.getElement()))
bfQueue.enqueue(temp);
}
}
},
insert: function(element, toElement, traversal) {
//Adds a node to a tree.
console.log("Inserting element: " + element)
var parent = null
var callback = function(node){
//console.log("Now comparing: " + node.getElement())
if (node.getElement() === toElement ){
//console.log(JSON.stringify("Found parent: " + node.getElement()) );
parent = node;
}
}
traversal.call(this, callback);
if(parent){
//console.log("Adding to tail: " + JSON.stringify(data));
var childrenList = parent.children
childrenList.addToTail(element);
var newChild = childrenList.getNode(childrenList.length-1)
newChild.parent = parent;
//console.log(JSON.stringify("Added new child to " + parent.getElement() + " with element " + newChild.getElement()) );
} else {
throw new Error('cannot add node to a non-existent parent')
}
}
Here is the LinkedList implementation:
module.exports = { Position, ListNode, SingleLinkedList}
function Position(element){
this._element = element
}
Position.prototype.getElement = function(){
return this._element
}
function ListNode(element, next){
this.next = next,
Position.call(this, element)
}
ListNode.prototype = Object.create(Position.prototype)
function SingleLinkedList(){
this.head = null;
this.length = 0;
}
SingleLinkedList.prototype = {
addToHead : function(element){
var newHead = new ListNode(element, this.head);
this.head = newHead;
this.length++
},
addToTail : function(element){
var newNode = new ListNode(element, null);
if (this.head === null){
//console.log("Head not found, adding to head")
this.head = newNode;
this.length++;
return;
}
var current_node = this.head;
while(!(current_node.next === null)){
current_node = current_node.next
}
current_node.next = newNode;
this.length++
},
getNode : function(position){
if (position >= this.length) {
console.log("Position out of bounds")
}
var nodeToCheck = this.head;
//console.log("Checking node: " + nodeToCheck.getElement())
var i = 0;
while(i != position) {
nodeToCheck = nodeToCheck.next;
//console.log("New nodeToCheck: " + nodeToCheck.getElement())
i++;
}
return nodeToCheck;
}
}
Started writing the removal function for an unbalanced BST structure. Manually running some tests for the first case (node has no children). Decided to run it on a tree of size 1 (just the root), and for some reason it does not seem to be reassigning the root to null the way I'm expecting it to on line 3 of this statement:
return direction ?
parent[direction] :
node = null;
Then when I run inOrderTraversal on the single node tree, which should just console.log each node, and return undefined for a null tree (what I'm expecting) it simply prints the 55 as it does before the removal.
It seems to be working for all other cases where the node to remove has no children.
Here's the fiddle: https://jsfiddle.net/uvdrmwh0/6/
And the code:
"use strict";
function Node(value, left = null, right = null) {
return {
value,
left,
right
};
}
function insert(x, root) {
let currNode = root;
while (currNode) {
if (x < currNode.value) {
if (currNode.left) {
currNode = currNode.left;
} else {
currNode.left = Node(x);
return;
}
} else if (x > currNode.value) {
if (currNode.right) {
currNode = currNode.right;
} else {
currNode.right = Node(x);
return;
}
} else if (x === currNode.value) {
throw new Error("cannot insert node with the same value as an existing node");
} else {
throw new Error("undefined behavior in insert");
}
}
throw new Error("failed to insert");
}
function remove(x, node, parent = null, direction = null) {
if (node === null) return;
if (node.value === x) {
if (!node.left && !node.right) {
return direction ?
parent[direction] = null :
node = null;
} else if (node.left && !node.right) {
//TODO
}
//TODO
}
direction = x < node.value ? "left" : "right";
remove(x, node[direction], node, direction);
}
function inOrderTraversal(node) {
if (node === null) return;
inOrderTraversal(node.left);
console.log(node.value);
inOrderTraversal(node.right);
}
function BinarySearchTree(seed) {
if (!Array.isArray(seed)) {
throw new Error("BinarySearchTree must be seeded with an array");
}
let root = Node(seed[0]);
seed.slice(1).forEach(x => {
insert(x, root);
});
return root;
}
let bst = BinarySearchTree([55]);
inOrderTraversal(bst);
console.log("---------after removal---------");
remove(55, bst);
inOrderTraversal(bst);
Update:
I've noticed things like this work:
let x = { a: 1 };
function changeProperty(obj, key, newValue) {
obj[key] = newValue;
}
changeProperty(x, "a", "hello");
console.log(x.a); //prints hello
But this doesn't:
function reassignObject(obj) {
obj = { a: "some new value" };
}
reassignObject(x);
console.log(x.a); //still prints hello
It seems you can reassign properties of an object (pointers within an object) and it will change the outside reference, but reassigning the reference to the object itself doesn't?
The following change should make it work:
console.log("---------after removal---------");
bst = remove(55, bst); //change here
The changes to node happen locally in remove function. So you should set the bst to whatever is received back from remove function.
The important thing to understand here is how does javascript pass the arguments. I hope this helps.
I'd like to create a recursive function to parse json-like data as below. When key is xtype, a new class will be created. In particular, when xtype = gridpanel/treepanel, all the properties have to be its constructor argument, otherwise, properties will be added after class has been created.
My recursive function as below, I got an error 'too much recursion' at line 21 in ext-all.js.
Please take a look, how am I able to solve this problem?
codes in main program:
me.recursiveParser(null, data.json);
Ext.apply(me.root, me.parent);
me.desktopCfg = me.root;
recursiveParser function:
recursiveParser: function(nodeName, jsonData) {
var properties = {};
var isSpecial = false;
var isLeaf = true;
var parent, child, special;
//Base factor
for (var key in jsonData) {
var value = jsonData[key];
//To collect all the properties that is only initialized with '#'.
if (key.toString().indexOf("#") === 0) {
key = key.replace("#", "");
if(typeof(value) === "string"){
properties[key] = "'"+value+"'";
}else{
//Later, should have to deal with the empty value or array with no keys and only elements.
properties[key] = value;
}
if(key === "xtype"){
//To initialize the root
if(nodeName === null){
this.root = this.createNewObject(value, null);
}
if(value === "gridpanel" || value === "treepanel"){
isSpecial = true;
special = value;
}else{
child = this.createNewObject(value, null);
}
}
}else {
isLeaf = false;
}
}
if(isSpecial){
child = this.createNewObject(special, properties);
}
//To add the subnode and its properties to its parent object.
if (nodeName !== null && typeof(nodeName) === "string") {
if(child === null){
Ext.apply(parent, properties);
}else{
Ext.apply(parent, child);
}
}
if(isLeaf){
return;
}
for (var key in jsonData) {
var value = jsonData[key];
if (key.toString().indexOf("#") === 0) {
continue;
}else{
if(value === "[object Object]"){
for(var index in value){
this.recursiveParser(key, value[index]);
}
}else{
this.recursiveParser(key, value);
}
Ext.apply(this.root, parent);
}
}
}
createNewObject function:
createNewObject: function(objType, properties){
if(objType){
switch (objType){
case "gridpanel":
return new MyProg.base.GridPanel(properties);
break;
case "treepanel":
return new MyProg.base.TreePanel(properties);
break;
case "tabpanel":
return new MyProg.base.TabPanel();
break;
case "tab":
return new MyProg.base.Tabs();
break;
case "formpanel":
return new MyProg.base.Accordion();
break;
case "fieldset":
return new MyProg.base.FieldSet();
break;
case "textfield":
return new MyProg.base.Fields();
break;
case "panel":
return new MyProg.base.Accordion();
break;
default:
return new MyProg.base.Accordion();
};
};
}
data.json:
var data = {
"json": {
"#title": "BusinessIntelligence",
"#xtype": "tab",
"#layout": "accordion",
"items": [
{
"#title": "SalesReport",
"#ctitle": "SalesReport",
"#layout": "column",
"items": [
{}
]
},
{
"#title": "ContentPlayingReport",
"#ctitle": "ContentPlayingReport",
"#layout": "column",
"items": [
{}
]
},
{
"#title": "BusinessIntelligence",
"#ctitle": "BusinessIntelligence",
"#layout": "column",
"items": [
{}
]
}
]
}
}
I modified the recursion part, it looks more elegant now. All the xtype works just fine, except gridpanel, I've check DOM, everything is in there, but still got error message:
TypeError: c is undefined
...+g.extraBaseCls);delete g.autoScroll;if(!g.hasView){if(c.buffered&&!c.remoteSort...
ext-all.js (line 21, col 1184416)
I suspect it's an ExtJS bug. I'll try to find another way out.
recursion program:
recursiveParser: function (jsonData) {
var me = this;
var properties = {};
for ( var key in jsonData ){
var value = jsonData[key];
var items = (value.constructor === Array) ? [] : {};
if (value instanceof Object) {
if (isNaN(key)){
if (items.constructor === Array) {
for (var node in value){
items.push(me.recursiveParser(value[node]));
}
properties[key] = items;
} else {
properties[key] = me.recursiveParser(value);
}
} else {
return me.recursiveParser(value);
}
} else {
if (key.toString().indexOf('#') === 0){
key = key.replace('#', '');
properties[key] = value;
}
}
}
return properties;
}