Rotation binary tree node is dropping half the tree - javascript

Im trying to build an AVL/Binary tree, but was not able to find a lot of code examples, only theory. My implementation for some reason has the AVL rotation function losing half the tree when the tree is 1 sided.
Here is my code:
function buildTree(dataSet){
let root = null
function rotateRight(node){
return rotate(node,true)
}
function rotateLeft(node){
return rotate(node,false)
}
function rotate(node,right){
const inputNodeSide = right ? "left" : "right"
const targetNodeSide = right ? "right" : "left"
const targetNode = node[ inputNodeSide]
const targetNodeChild = targetNode[targetNodeSide]
targetNode[targetNodeSide] = node
node[ inputNodeSide] = targetNodeChild
return targetNode // as this is at the top
}
function createNode(data){
return {
data,
left : null,
right : null,
get balance(){
return workOutHeight(this.left) -
workOutHeight(this.right)
},
get height(){
return workOutHeight(this)
},
}
} // END createNode
function workOutHeight(node){
if(null === node){
return -1
}
return Math.max(workOutHeight(node.left),
workOutHeight(node.right))+1
}
function avl(node){
const balanced = node.balance
if(2 === balanced){
if(0 > node.left.balance){
node.left = rotateLeft(node.left);
}
return rotateRight(node);
} else if(-2 === balanced){
if(0 < node.right.balance){
node.right = rotateRight(node.right);
}
return rotateLeft(node);
}
return node
}
this.add = function(data, perent){
perent = perent || root;
if(null === perent){
return root = createNode(data);
} else if(data < perent.data){
if(null === perent.left){
return perent.left = createNode(data);
}
this.add(data,perent.left)
avl(perent)
} else if(data > perent.data){
if(null === perent.right){
return perent.right = createNode(data);
}
this.add(data,perent.right)
avl(perent)
}
} // END addData
this.tree = function(){
return JSON.parse(JSON.stringify(root))
}
if(Array.isArray(dataSet)){
dataSet.forEach(val=>this.add(val))
}
} // END buildTree
console.log(new buildTree([2,6,9,4,7,0]).tree())
Thanks for any help on this.

The problem is in the this.add method.
It calls avl ignoring the node that is returned by that call. This returned node should replace the node that was passed as argument. As you don't know where this returned node should be attached to, you should return it as the return value for this.add, and that means you should redesign that whole function to deal with this "return" feature.
Here is how it could be adapted:
this.add = function(data, perent=root) {
if (!perent) {
return createNode(data);
} else if(data < perent.data) {
perent.left = this.add(data, perent.left);
} else if(data > perent.data) {
perent.right = this.add(data, perent.right);
}
return avl(perent);
}
Note that I removed the assignment to root here. I would suggest to move that to the place where you make the initial call to this.add:
if (Array.isArray(dataSet)) {
dataSet.forEach(val => root=this.add(val));
}
And now it will work.
Redesign
As JavaScript has since ECMAScript 2015 the class syntax, I would actually make use of that, and create a Tree and Node class. Private members can be created with the # syntax.
Also, it is not efficient to calculate the height of a node every moment you need to calculate the balance factor of a node. The nice thing of AVL trees is that when you know the balance factors of the nodes that are being rotated, you can derive from that their new balance factors -- without needing the actual heights.
Here is all of that put in code, with in addition an input box, with which you can add nodes to the tree. The tree is displayed with simple indentation format (with the root at the left side).
class Tree {
#root = null;
static #config = {
"2": { "0": [ 1, -1], "1": [ 0, 0], "2": [-1, 0] },
"-2": { "0": [-1, 1],"-1": [ 0, 0],"-2": [ 1, 0] },
"1": {"-1": [ 0, -2], "0": [ 0, -1], "1": [-1, -1] },
"-1": { "1": [ 0, 2], "0": [ 0, 1],"-1": [ 1, 1] },
};
static #Node = class {
constructor(data) {
this.data = data;
this.left = this.right = null;
this.balance = 0;
}
add(data) {
const side = data < this.data ? "left" : "right";
let delta = data < this.data ? -1 : 1;
if (!this[side]) {
this[side] = new Tree.#Node(data);
} else {
let balance = Math.abs(this[side].balance);
this[side] = this[side].add(data);
if (balance || !this[side].balance) delta = 0;
}
this.balance += delta;
return this.avl();
}
avl() {
const balanced = this.balance;
if (-2 === balanced) {
if (0 < this.left.balance) {
this.left = this.left.rotateLeft();
}
return this.rotateRight();
} else if (2 === balanced) {
if (0 > this.right.balance) {
this.right = this.right.rotateRight();
}
return this.rotateLeft();
}
return this;
}
rotateRight() {
return this.rotate("left", "right");
}
rotateLeft() {
return this.rotate("right", "left");
}
rotate(inputSide, targetSide) {
const target = this[inputSide];
const child = target[targetSide];
target[targetSide] = this;
this[inputSide] = child;
// Use a lookup table to determine how the balance is impacted by rotation
[this.balance, target.balance] = Tree.#config[this.balance][target.balance];
return target;
}
toString(indent="\n") {
return (this.right?.toString(indent + " ") ?? "")
+ indent + this.data
+ (this.left?.toString(indent + " ") ?? "");
}
}
constructor(...values) {
this.add(...values);
}
add(...values) {
for (const data of values) {
this.#root = this.#root?.add(data) ?? new Tree.#Node(data);
}
}
toString() {
return this.#root?.toString() ?? "<empty>";
}
}
const tree = new Tree;
// I/O handling
document.querySelector("button").addEventListener("click", () => {
const input = document.querySelector("input");
tree.add(+input.value);
input.value = "";
input.focus();
document.querySelector("pre").textContent = tree;
});
<input type="number"><button>Add</button><br>
<pre></pre>

Related

A*(A-star) algorithm gives wrong path and crashes

I'm implementing A*(A-star) algorithm in react.js but my program crashes whenever startNode(green) or destinationNode(blue) have more than one neighbour or if there is a cycle in the graph. There is something wrong when adding and deleting the neighbours from/to openList or when updating the parentId in the getPath() function. I cant even see the console because the website goes down.
Each node has: id, name, x, y, connectedToIdsList:[], gcost:Infinity, fcost:0, heuristic:0, parentId:null.
I'm sending the path to another component "TodoList" which prints out the path. My program should not return the path but keeps updating the path as i'm adding nodes and edges to the list. Please help, I've been stuck for hours now:/
My code:
export default class TurnByTurnComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = { shortestPath: [] }
}
render() {
const {
destinationLocationId,
locations,
originLocationId
} = this.props;
let path = []
if (destinationLocationId != null && originLocationId != null) {
if (originLocationId == destinationLocationId) { //check if the startNode node is the end node
return originLocationId;
}
var openList = [];
let startNode = getNodeById(originLocationId);
let destinationNode = getNodeById(destinationLocationId)
if (startNode.connectedToIds.length > 0 && destinationNode.connectedToIds.length > 0) { //check if start and destination nodes are connected first
startNode.gcost = 0
startNode.heuristic = manhattanDistance(startNode, destinationNode)
startNode.fcost = startNode.gcost + startNode.heuristic;
//perform A*
openList.push(startNode); //starting with the startNode
while (openList.length > 0) {
console.log("inside while")
var currentNode = getNodeOfMinFscore(openList); //get the node of the minimum f cost of all nodes in the openList
if (currentIsEqualDistanation(currentNode)) {
path = getPath(currentNode);
}
deleteCurrentFromOpenList(currentNode, openList);
for (let neighbourId of currentNode.connectedToIds) {
var neighbourNode = getNodeById(neighbourId);
currentNode.gcost = currentNode.gcost + manhattanDistance(currentNode, neighbourNode);
if (currentNode.gcost < neighbourNode.gcost) {
neighbourNode.parentId = currentNode.id; // keep track of the path
// total cost saved in neighbour.g
neighbourNode.gcost = currentNode.gcost;
neighbourNode.heuristic = manhattanDistance(neighbourNode, destinationNode);
neighbourNode.fcost = neighbourNode.gcost + neighbourNode.heuristic; //calculate f cost of the neighbourNode
addNeighbourNodeToOpenList(neighbourNode, openList);
}
}
}
path = path.reverse().join("->");
}
}
function addNeighbourNodeToOpenList(neighbourNode, openList) {
//add neighbourNode to the open list to be discovered later
if (!openList.includes(neighbourNode)) {
openList.push(neighbourNode);
}
}
function deleteCurrentFromOpenList(currNode, openList) {
const currIndex = openList.indexOf(currNode);
openList.splice(currIndex, 1); //deleting currentNode from openList
}
function currentIsEqualDistanation(currNode) {
//check if we reached out the distanation node
return (currNode.id == destinationLocationId)
}
function getNodeById(nid) {
var node;
for (let i = 0; i < locations.length; i++) {
if (locations[i].id == nid) {
node = locations[i]
}
}
return node
}
function getPath(destNode) {
console.log("inside getpath")
var parentPath = []
var parent;
while (destNode.parentId != null) {
parentPath.push(destNode.name)
parent = destNode.parentId;
destNode = getNodeById(parent);
}
//adding startNode to the path
parentPath.push(getNodeById(originLocationId).name)
return parentPath;
}
function getNodeOfMinFscore(openList) {
var minFscore = openList[0].fcost; //initValue
var nodeOfminFscore;
for (let i = 0; i < openList.length; i++) {
if (openList[i].fcost <= minFscore) {
minFscore = openList[i].fcost //minFvalue
nodeOfminFscore = openList[i]
}
}
return nodeOfminFscore
}
//manhattan distance is for heuristic and gScore. Here I use Manhattan instead of Euclidean
//because in this example we dont have diagnosal path.
function manhattanDistance(stNode, dstNode) {
var x = Math.abs(dstNode.x - stNode.x);
var y = Math.abs(dstNode.y - stNode.y);
var dist = x + y;
return dist;
}
return (
<div className="turn-by-turn-component">
<TodoList
list={"Shortest path: ", [path]}
/>
<TodoList
list={[]}
/>
</div>
);
}
}
TurnByTurnComponent.propTypes = {
destinationLocationId: PropTypes.number,
locations: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
connectedToIds: PropTypes.arrayOf(PropTypes.number.isRequired).isRequired
})),
originLocationId: PropTypes.number
};
Update new issue
Pictures of before and after linking a new node. When I update the graph and add a new node the path disappears. And sometimes come back and so on if I still add new nodes and edges to the graph. As I said, each node has: id, name, x, y, connectedToIdsList:[], gcost:Infinity, fcost:0, heuristic:0, parentId:null.
My new code now is:
export default class TurnByTurnComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = { shortestPath: [] }
}
render() {
const {
destinationLocationId,
locations,
originLocationId
} = this.props;
let path = []
if (destinationLocationId != null && originLocationId != null) {
console.log(JSON.stringify(locations))
path = [getNodeById(originLocationId).namne];
if (originLocationId != destinationLocationId) {
var openList = [];
let startNode = getNodeById(originLocationId);
let destinationNode = getNodeById(destinationLocationId)
if (startNode.connectedToIds.length > 0 && destinationNode.connectedToIds.length > 0) {
startNode.gcost = 0
startNode.heuristic = manhattanDistance(startNode, destinationNode)
startNode.fcost = startNode.gcost + startNode.heuristic;
openList.push(startNode); //starting with the startNode
while (openList.length > 0) {
var currentNode = getNodeOfMinFscore(openList); //get the node of the minimum f cost of all nodes in the openList
if (currentIsEqualDistanation(currentNode)) {
path = getPath(currentNode);
break;
}
deleteCurrentFromOpenList(currentNode, openList);
for (let neighbourId of currentNode.connectedToIds) {
var neighbourNode = getNodeById(neighbourId);
let gcost = currentNode.gcost + manhattanDistance(currentNode, neighbourNode);
if (gcost < (neighbourNode.gcost ?? Infinity)) {
neighbourNode.parentId = currentNode.id;
// keep track of the path
// total cost saved in neighbour.g
neighbourNode.gcost = gcost;
neighbourNode.heuristic = manhattanDistance(neighbourNode, destinationNode);
neighbourNode.fcost = neighbourNode.gcost + neighbourNode.heuristic; //calculate f cost of the neighbourNode
addNeighbourNodeToOpenList(neighbourNode, openList);
}
}
}
}
}
}
path = path.reverse().join("->");
function addNeighbourNodeToOpenList(neighbourNode, openList) {
//add neighbourNode to the open list to be discovered later
if (!openList.includes(neighbourNode)) {
openList.push(neighbourNode);
}
}
function deleteCurrentFromOpenList(currentNode, openList) {
const currIndex = openList.indexOf(currentNode);
openList.splice(currIndex, 1); //deleting currentNode from openList
}
function currentIsEqualDistanation(currentNode) {
//check if we reached out the distanation node
return (currentNode.id == destinationLocationId)
}
function getNodeById(id) {
var node;
for (let i = 0; i < locations.length; i++) {
if (locations[i].id == id) {
node = locations[i]
}
}
return node
}
function getPath(destinationNode) {
console.log("inside getpath")
var parentPath = []
var parent;
while (destinationNode.parentId != null) {
parentPath.push(destinationNode.name)
parent = destinationNode.parentId;
destinationNode = getNodeById(parent);
}
//adding startNode to the path
parentPath.push(getNodeById(originLocationId).name)
return parentPath;
}
function getNodeOfMinFscore(openList) {
var minFscore = openList[0].fcost; //initValue
var nodeOfminFscore;
for (let i = 0; i < openList.length; i++) {
if (openList[i].fcost <= minFscore) {
minFscore = openList[i].fcost //minFvalue
nodeOfminFscore = openList[i]
}
}
return nodeOfminFscore
}
//manhattan distance is for heuristic and gScore. Here I use Manhattan instead of Euclidean
//because in this example we dont have diagnosal path.
function manhattanDistance(startNode, destinationNode) {
var x = Math.abs(destinationNode.x - startNode.x);
var y = Math.abs(destinationNode.y - startNode.y);
var dist = x + y;
return dist;
}
return (
<div className="turn-by-turn-component">
<TodoList
title="Mandatory work"
list={[path]}
/>
<TodoList
title="Optional work"
list={[]}
/>
</div>
);
}
}
TurnByTurnComponent.propTypes = {
destinationLocationId: PropTypes.number,
locations: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
connectedToIds: PropTypes.arrayOf(PropTypes.number.isRequired).isRequired
})),
originLocationId: PropTypes.number
};
There are a few issues in your code:
return originLocationId; should not happen. When the source is equal to the target, then set the path, and make sure the final return of this function is executed, as you want to return the div element.
When the target is found in the loop, not only should the path be built, but the loop should be exited. So add a break
currentNode.gcost = currentNode.gcost + manhattanDistance(currentNode, neighbourNode); is not right: you don't want to modify currentNode.gcost: its value should not depend on the outgoing edge to that neighbour. Instead store this sum in a temporary variable (like gcost)
The comparison with neighbourNode.gcost will not work when that node does not yet have a gcost member. I don't see in your code that it got a default value that makes sure this condition is true, so you should use a default value here, like (neighbourNode.gcost ?? Infinity)
path = path.reverse().join("->"); should better be executed always, also when there is no solution (so path is a string) or when the source and target are the same node.
Here is a corrected version, slightly adapted to run here as a runnable snippet:
function render() {
const {
destinationLocationId,
locations,
originLocationId
} = this.props;
let path = [];
if (destinationLocationId != null && originLocationId != null) {
path = [originLocationId]; // The value for when the next if condition is not true
if (originLocationId != destinationLocationId) {
var openList = [];
let startNode = getNodeById(originLocationId);
let destinationNode = getNodeById(destinationLocationId)
if (startNode.connectedToIds.length > 0 && destinationNode.connectedToIds.length > 0) {
startNode.gcost = 0
startNode.heuristic = manhattanDistance(startNode, destinationNode)
startNode.fcost = startNode.gcost + startNode.heuristic;
openList.push(startNode);
while (openList.length > 0) {
var currentNode = getNodeOfMinFscore(openList);
if (currentIsEqualDistanation(currentNode)) {
path = getPath(currentNode);
break; // Should end the search here!
}
deleteCurrentFromOpenList(currentNode, openList);
for (let neighbourId of currentNode.connectedToIds) {
var neighbourNode = getNodeById(neighbourId);
// Should not modify the current node's gcost. Use a variable instead:
let gcost = currentNode.gcost + manhattanDistance(currentNode, neighbourNode);
// Condition should also work when neighbour has no gcost yet:
if (gcost < (neighbourNode.gcost ?? Infinity)) {
neighbourNode.parentId = currentNode.id;
neighbourNode.gcost = gcost; // Use the variable
neighbourNode.heuristic = manhattanDistance(neighbourNode, destinationNode);
neighbourNode.fcost = neighbourNode.gcost + neighbourNode.heuristic;
addNeighbourNodeToOpenList(neighbourNode, openList);
}
}
}
}
}
}
// Convert the path to string in ALL cases:
path = path.reverse().join("->");
function addNeighbourNodeToOpenList(neighbourNode, openList) {
if (!openList.includes(neighbourNode)) {
openList.push(neighbourNode);
}
}
function deleteCurrentFromOpenList(currNode, openList) {
const currIndex = openList.indexOf(currNode);
openList.splice(currIndex, 1);
}
function currentIsEqualDistanation(currNode) {
return (currNode.id == destinationLocationId)
}
function getNodeById(nid) {
var node;
for (let i = 0; i < locations.length; i++) {
if (locations[i].id == nid) {
node = locations[i]
}
}
return node
}
function getPath(destNode) {
var parentPath = []
var parentId;
while (destNode.parentId != null) {
parentPath.push(destNode.name)
parentId = destNode.parentId;
destNode = getNodeById(parentId);
}
parentPath.push(getNodeById(originLocationId).name)
return parentPath;
}
function getNodeOfMinFscore(openList) {
var minFscore = openList[0].fcost;
var nodeOfminFscore;
for (let i = 0; i < openList.length; i++) {
if (openList[i].fcost <= minFscore) {
minFscore = openList[i].fcost
nodeOfminFscore = openList[i]
}
}
return nodeOfminFscore
}
function manhattanDistance(stNode, dstNode) {
var x = Math.abs(dstNode.x - stNode.x);
var y = Math.abs(dstNode.y - stNode.y);
var dist = x + y;
return dist;
}
return "Shortest path: " + path;
}
// Demo
let props = {
locations: [
{ id: 1, x: 312, y: 152, connectedToIds: [4,2], name: "Thetaham" },
{ id: 2, x: 590, y: 388, connectedToIds: [1,3], name: "Deltabury" },
{ id: 3, x: 428, y: 737, connectedToIds: [2], name: "Gammation" },
{ id: 4, x: 222, y: 430, connectedToIds: [1], name: "Theta City" },
],
originLocationId: 1,
destinationLocationId: 3,
};
console.log(render.call({props}));

Convert JSON to CSV and have the Headers be concatenations of the parents

I want to convert JSON responses to CSV format. Since the JSON response may differ, I can't manually create my headers.
I looked around and found a programmatic solution on stack overflow. But this solution uses the keys of the JSON as headers. My test data is sensitive so I'll post a similar JSON response that gives a better idea of what I'm trying to do.
{
"response":[{
"_id": "5cfe7d3c6deeeef08ce0444b",
"name": "Debra Milligain",
"phone": "+1 (906) 432-2182",
"address": "676 Merit Court, Steinhatchee, Oregon, 5491",
"tags": [
"consequat",
"reprehenderit",
"amet"
],
"Work": {
"skills": [{
"id": 0,
"name": "Programming"
},
{
"id": 1,
"name": "Business"
}
]
},
"friends": [{
"id": 0,
"name": "Stafford Hernandez"
},
{
"id": 1,
"name": "Colleen Christensen"
},
{
"id": 2,
"name": "Barker Keith"
}
],
"greeting": [],
"favoriteFruit": "banana"
}]}
Solution found from hereChristian Landgren 's Solution
function json2csv(json) {
const items = json.response;
const replacer = (key, value) => value === null ? '' : value // specify how you want to handle null values here
const header = Object.keys(items[0])
let csv = items.map(row => header.map(fieldName => JSON.stringify(row[fieldName], replacer)).join(','))
csv.unshift(header.join(','))
csv = csv.join('\r\n')
//console.log(csv)
}
This is the actual response:
_id,name,phone,address,tags,Work,friends,greeting,favoriteFruit
"5cfe7d3c6deeeef08ce0444b","Debra Milligain","+1 (906) 432-2182","676 Merit Court, Steinhatchee, Oregon, 5491",["consequat","reprehenderit","amet"],{"skills":[{"id":0,"name":"Programming"},{"id":1,"name":"Business"}]},[{"id":0,"name":"Stafford Hernandez"},{"id":1,"name":"Colleen Christensen"},{"id":2,"name":"Barker Keith"}],[],"banana"
The output as the headers but doesn't include the sub headers. The actual output should be this more or less.
"_id","name","phone","address","tags","Work__skills__id","Work__skills__name","friends__id","friends__name","favoriteFruit"
"5cfe7d3c6deeeef08ce0444b","Debra Milligain","+1 (906) 432-2182","676 Merit Court, Steinhatchee, Oregon, 5491","consequat","0","Programming","0","Stafford Hernandez","banana"
"","","","","reprehenderit","1","Business","1","Colleen Christensen",""
"","","","","amet","","","2","Barker Keith",""
What about more object oriented approach with recursion hidden in constructor ?
Made iterator - you can iterate 4 directions (next, prev, node, parent) and also 2 demo traverse of your sample data (log is too long for single output).
And in case you get private Current JNode out of iterator, you can list that container structure in any debugger where you can inspect variables (example VSC & node.js).
Separate IIFE code is in 3rd box.
DepthFirst demo:
'use strict';
function test(json) {
var it = new JIterator(json);
var i = 0;
var htmlTable = "<table border=1><tr><th>#</th><th>Level</th><th>Path</th><th>Key</th><th>Value or Type</th></tr>";
do {
htmlTable += "<tr><td>";
htmlTable += [i++, it.Level, it.Path().join('.'), it.KeyDots(), (it.Value() instanceof Object) ? (it.Value() instanceof Array ? "[]" : "{}") : it.Value()].join("</td><td>");
htmlTable += "</td></tr>";
} while (it.DepthFirst());
htmlTable += "</table>";
document.body.innerHTML = htmlTable;
}
var JNode = (function (jsNode) {
function JNode(_parent, _pred, _key, _value) {
this.parent = _parent;
this.pred = _pred;
this.node = null;
this.next = null;
this.key = _key;
this.value = _value;
}
return JNode;
})();
var JIterator = (function (json) {
var root, current, maxLevel = -1;
function JIterator(json, parent) {
if (parent === undefined) parent = null;
var pred = null, localCurrent;
for (var child in json) {
var obj = json[child] instanceof Object;
if(json instanceof Array) child = parseInt(child); // non-associative array
if (!root) root = localCurrent = new JNode(parent, null, child, json[child]);
else {
localCurrent = new JNode(parent, pred, child, obj ? ((json[child] instanceof Array) ? [] : {}) : json[child]);
}
if (pred) pred.next = localCurrent;
if (parent && parent.node == null) parent.node = localCurrent;
pred = localCurrent;
if (obj) {
var memPred = pred;
JIterator(json[child], pred);
pred = memPred;
}
}
if (this) {
current = root;
this.Level = 0;
}
}
JIterator.prototype.Current = function () { return current; }
JIterator.prototype.Parent = function () {
var retVal = current.parent;
if (retVal == null) return false;
this.Level--;
return current = retVal;
}
JIterator.prototype.Pred = function () {
var retVal = current.pred;
if (retVal == null) return false;
return current = retVal;
}
JIterator.prototype.Node = function () {
var retVal = current.node;
if (retVal == null) return false;
this.Level++;
return current = retVal;
}
JIterator.prototype.Next = function () {
var retVal = current.next;
if (retVal == null) return false;
return current = retVal;
}
JIterator.prototype.Key = function () { return current.key; }
JIterator.prototype.KeyDots = function () { return (typeof(current.key) == "number")?"":(current.key+':'); }
JIterator.prototype.Value = function () { return current.value; }
JIterator.prototype.Reset = function () {
current = root;
this.Level = 0;
}
JIterator.prototype.RawPath = function () {
var steps = [], level = current;
do {
if (level != null && level.value instanceof Object) {
steps.push(level.key + (level.value instanceof Array ? "[]" : "{}"));
} else {
if (level != null) steps.push(level.key);
else break;
}
level = level.parent;
} while (level != null);
var retVal = "";
retVal = steps.reverse();
return retVal;
}
JIterator.prototype.Path = function () {
var steps = [], level = current;
do {
if (level != null && level.value instanceof Object) {
var size = 0;
var items = level.node;
if(typeof(level.key) == "number") steps.push('[' + level.key + ']');
else {
while(items) {
size++;
items = items.next;
}
var type = (level.value instanceof Array ? "[]" : "{}");
var prev = steps[steps.length-1];
if(prev && prev[0] == '[') {
var last = prev.length-1;
if(prev[last] == ']') {
last--;
if(!isNaN(prev.substr(1, last))) {
steps.pop();
size += '.' + prev.substr(1, last);
}
}
}
steps.push(level.key + type[0] + size + type[1]);
}
} else {
if (level != null) {
if(typeof(level.key) == "number") steps.push('[' + level.key + ']');
else steps.push(level.key);
}
else break;
}
level = level.parent;
} while (level != null);
var retVal = "";
retVal = steps.reverse();
return retVal;
}
JIterator.prototype.DepthFirst = function () {
if (current == null) return 0; // exit sign
if (current.node != null) {
current = current.node;
this.Level++;
if (maxLevel < this.Level) maxLevel = this.Level;
return 1; // moved down
} else if (current.next != null) {
current = current.next;
return 2; // moved right
} else {
while (current != null) {
if (current.next != null) {
current = current.next;
return 3; // returned up & moved next
}
this.Level--;
current = current.parent;
}
}
return 0; // exit sign
}
JIterator.prototype.BreadthFirst = function () {
if (current == null) return 0; // exit sign
if (current.next) {
current = current.next;
return 1; // moved right
} else if (current.parent) {
var level = this.Level, point = current;
while (this.DepthFirst() && level != this.Level);
if (current) return 2; // returned up & moved next
do {
this.Reset();
level++;
while (this.DepthFirst() && level != this.Level);
if (current) return 3; // returned up & moved next
} while (maxLevel >= level);
return current != null ? 3 : 0;
} else if (current.node) {
current = current.node;
return 3;
} else if (current.pred) {
while (current.pred) current = current.pred;
while (current && !current.node) current = current.next;
if (!current) return null;
else return this.DepthFirst();
}
}
return JIterator;
})();
var json = {
"_id": "5cfe7d3c6deeeef08ce0444b",
"name": "Debra Milligain",
"phone": "+1 (906) 432-2182",
"address": "676 Merit Court, Steinhatchee, Oregon, 5491",
"tags": [
"consequat",
"reprehenderit",
"amet"
],
"Work": {
"skills": [
{
"id": 0,
"name": "Programming"
},
{
"id": 1,
"name": "Business"
}
]
},
"friends": [
{
"id": 0,
"name": "Stafford Hernandez"
},
{
"id": 1,
"name": "Colleen Christensen"
},
{
"id": 2,
"name": "Barker Keith"
}
],
"greeting": [],
"favoriteFruit": "banana"
}
test(json);
table {
border-spacing: 0px; /* small tricks 2 make rounded table simply or */
}
th {
text-align:left; /* centered looks ugly */
}
td.empty {
background-color:lightgray; /* mark null cells */
}
BreadthFirst demo:
'use strict';
function test(json) {
var it = new JIterator(json);
var i = 0;
var htmlTable = "<table border=1><tr><th>#</th><th>Level</th><th>Path</th><th>Key</th><th>Value or Type</th></tr>";
do {
htmlTable += "<tr><td>";
htmlTable += [i++, it.Level, it.Path().join('.'), it.KeyDots(), (it.Value() instanceof Object) ? (it.Value() instanceof Array ? "[]" : "{}") : it.Value()].join("</td><td>");
htmlTable += "</td></tr>";
} while (it.BreadthFirst())
htmlTable += "</table>";
document.body.innerHTML = htmlTable;
}
var JNode = (function (jsNode) {
function JNode(_parent, _pred, _key, _value) {
this.parent = _parent;
this.pred = _pred;
this.node = null;
this.next = null;
this.key = _key;
this.value = _value;
}
return JNode;
})();
var JIterator = (function (json) {
var root, current, maxLevel = -1;
function JIterator(json, parent) {
if (parent === undefined) parent = null;
var pred = null, localCurrent;
for (var child in json) {
var obj = json[child] instanceof Object;
if(json instanceof Array) child = parseInt(child); // non-associative array
if (!root) root = localCurrent = new JNode(parent, null, child, json[child]);
else {
localCurrent = new JNode(parent, pred, child, obj ? ((json[child] instanceof Array) ? [] : {}) : json[child]);
}
if (pred) pred.next = localCurrent;
if (parent && parent.node == null) parent.node = localCurrent;
pred = localCurrent;
if (obj) {
var memPred = pred;
JIterator(json[child], pred);
pred = memPred;
}
}
if (this) {
current = root;
this.Level = 0;
}
}
JIterator.prototype.Current = function () { return current; }
JIterator.prototype.Parent = function () {
var retVal = current.parent;
if (retVal == null) return false;
this.Level--;
return current = retVal;
}
JIterator.prototype.Pred = function () {
var retVal = current.pred;
if (retVal == null) return false;
return current = retVal;
}
JIterator.prototype.Node = function () {
var retVal = current.node;
if (retVal == null) return false;
this.Level++;
return current = retVal;
}
JIterator.prototype.Next = function () {
var retVal = current.next;
if (retVal == null) return false;
return current = retVal;
}
JIterator.prototype.Key = function () { return current.key; }
JIterator.prototype.KeyDots = function () { return (typeof(current.key) == "number")?"":(current.key+':'); }
JIterator.prototype.Value = function () { return current.value; }
JIterator.prototype.Reset = function () {
current = root;
this.Level = 0;
}
JIterator.prototype.RawPath = function () {
var steps = [], level = current;
do {
if (level != null && level.value instanceof Object) {
steps.push(level.key + (level.value instanceof Array ? "[]" : "{}"));
} else {
if (level != null) steps.push(level.key);
else break;
}
level = level.parent;
} while (level != null);
var retVal = "";
retVal = steps.reverse();
return retVal;
}
JIterator.prototype.Path = function () {
var steps = [], level = current;
do {
if (level != null && level.value instanceof Object) {
var size = 0;
var items = level.node;
if(typeof(level.key) == "number") steps.push('[' + level.key + ']');
else {
while(items) {
size++;
items = items.next;
}
var type = (level.value instanceof Array ? "[]" : "{}");
var prev = steps[steps.length-1];
if(prev && prev[0] == '[') {
var last = prev.length-1;
if(prev[last] == ']') {
last--;
if(!isNaN(prev.substr(1, last))) {
steps.pop();
size += '.' + prev.substr(1, last);
}
}
}
steps.push(level.key + type[0] + size + type[1]);
}
} else {
if (level != null) {
if(typeof(level.key) == "number") steps.push('[' + level.key + ']');
else steps.push(level.key);
}
else break;
}
level = level.parent;
} while (level != null);
var retVal = "";
retVal = steps.reverse();
return retVal;
}
JIterator.prototype.DepthFirst = function () {
if (current == null) return 0; // exit sign
if (current.node != null) {
current = current.node;
this.Level++;
if (maxLevel < this.Level) maxLevel = this.Level;
return 1; // moved down
} else if (current.next != null) {
current = current.next;
return 2; // moved right
} else {
while (current != null) {
if (current.next != null) {
current = current.next;
return 3; // returned up & moved next
}
this.Level--;
current = current.parent;
}
}
return 0; // exit sign
}
JIterator.prototype.BreadthFirst = function () {
if (current == null) return 0; // exit sign
if (current.next) {
current = current.next;
return 1; // moved right
} else if (current.parent) {
var level = this.Level, point = current;
while (this.DepthFirst() && level != this.Level);
if (current) return 2; // returned up & moved next
do {
this.Reset();
level++;
while (this.DepthFirst() && level != this.Level);
if (current) return 3; // returned up & moved next
} while (maxLevel >= level);
return current != null ? 3 : 0;
} else if (current.node) {
current = current.node;
return 3;
} else if (current.pred) {
while (current.pred) current = current.pred;
while (current && !current.node) current = current.next;
if (!current) return null;
else return this.DepthFirst();
}
}
return JIterator;
})();
var json = {
"_id": "5cfe7d3c6deeeef08ce0444b",
"name": "Debra Milligain",
"phone": "+1 (906) 432-2182",
"address": "676 Merit Court, Steinhatchee, Oregon, 5491",
"tags": [
"consequat",
"reprehenderit",
"amet"
],
"Work": {
"skills": [
{
"id": 0,
"name": "Programming"
},
{
"id": 1,
"name": "Business"
}
]
},
"friends": [
{
"id": 0,
"name": "Stafford Hernandez"
},
{
"id": 1,
"name": "Colleen Christensen"
},
{
"id": 2,
"name": "Barker Keith"
}
],
"greeting": [],
"favoriteFruit": "banana"
}
test(json);
table {
border-spacing: 0px; /* small tricks 2 make rounded table simply or */
}
th {
text-align:left; /* centered looks ugly */
}
td.empty {
background-color:lightgray; /* mark null cells */
}
My JScript iterators moved to GitHub
I ended up using my own solution and just recursively going down the JSON object. I made sure to keep track of key-value pairs that were at the root of the object vs those that weren't (since I would need to append their parent names to them). Furthermore, I used a hash table to make sure i don't have duplicate headers by making them keys and made their values just be actual values of that header append as a string separated by a ,
to later be parsed. Here's the code, I decided to leave the console logs just for others to debug and understand the process.
var arrayOfHeaders = {};
var headerDirectory = "";
var rootLevel = true;
var temp = ""
function traverseJSON(obj){
for (var o in obj) {
if (typeof obj[o] == "object") {
//console.log("Before traversal ", o)
//console.log("Traversing the object: ", obj[o])
if(!isNaN(o)){
//console.log("Current position is a number ", o)
}else{
//console.log("Adding to directory... " , o)
headerDirectory += (headerDirectory == "") ? o : "_" + o;
}
rootLevel = false;
traverseJSON(obj[o]);
rootLevel = true;
temp = headerDirectory;
headerDirectory = "";
} else {
if (rootLevel) {
headerDirectory = "";
//console.log("Has value and is root ", o)
arrayOfHeaders[headerDirectory];
}
else {
//console.log("Has value and is not root ", o)
//console.log("Current Header Directory " + headerDirectory)
//console.log("Saved temp : ", temp)
if(isNaN(o)){
if(headerDirectory == "") headerDirectory = temp;
//arrayOfHeaders.push(headerDirectory + "_" + o)
arrayOfHeaders[headerDirectory + "_" + o] += ",\"" + obj[o] + "\"";
}
}
}
}
// console.log("Array of Headers : ", arrayOfHeaders)
}
I used the same sample data provided in the question and the here's the contents of arrayOfHeaders after running the method.
Array of Headers : { 'Work_skills_id-skill': 'undefined,"0","Business"',
'Work_skills_name-skill': 'undefined,"Programming"',
'friends_id-friends': 'undefined,"0","1","2"',
'friends_name-friends':'undefined,"Stafford Hernandez","Colleen Christensen","Barker Keith"' }
Hopefully this helps others with a similar problem.

How to make binary tree from array in javascript?

I have an array var array = [8,10,12,5,3,6];
Logic
First node would be root node.
If new node value is less or equal =< than parent node, It would be left node of parent node
If new node value is greater > than parent node, It would be right node of parent node
And I am trying to achieve output like below object:
{
value:8,
left:{
value:5,
left:{ value:3 },
right:{value:6}
},
right:{
value:10,
right:{value:12}
}
}
Which would be in image like this
I tried below code:
var arr = [8,10,12,5,3,6];
var root = arr[0];
var rv = {};
for (var i = 0; i < arr.length; i++){
if(arr[i] < root){
rv.left = arr[i];
}else{
rv.right = arr[i];
}
}
console.log(rv);
Please help me to solve this.
You could use a Node instance for new nodes and a function for inserting nodes.
Then iterate the values and build a new tree.
function Node(value) {
this.value = value;
// this.left = null;
// this.right = null;
}
function insertNode(tree, value) {
var node = tree,
key;
while (node.value !== value) {
key = value < node.value ? 'left' : 'right';
if (!node[key]) {
node[key] = new Node(value);
break;
}
node = node[key];
}
return tree;
}
var array = [8, 10, 12, 5, 3, 6],
tree = array.reduce((t, v) => t ? insertNode(t, v) : new Node(v), null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
While it is close to Nina's answer i believe to be a little more concise;
var data = [8,10,12,5,3,6],
tree;
function insertBinTree (t = {value: void 0, left: void 0, right: void 0}, n){
t.value !== void 0 ? t.value > n ? t.left = insertBinTree(t.left,n)
: t.right = insertBinTree(t.right,n)
: t.value = n;
return t;
}
tree = data.reduce(insertBinTree, void 0);
console.log(tree);
.as-console-wrapper {
max-height: 100% !important
}
Try something like this by recursive method
var binary = {};
var arr = [8,5,10,3,6,12];
function makeBinary(binary,number){
if(binary.value === undefined){
binary.value = number;
}else if(number > binary.value){
if(binary.right === undefined){
binary.right = {value:number};
}else{
binary.right = makeBinary(binary.right,number);
}
}else{
if(binary.left === undefined){
binary.left = {value:number};
}else{
binary.left = makeBinary(binary.left,number);
}
}
return binary;
}
for(let i in arr){
makeBinary(binary,arr[i]);
}
console.log(binary);
class Node {
constructor(val){
this.val=val;
this.right=null;
this.left=null;
}
}
class Bst{
constructor(){
this.root=null;
}
insert(val){
let newNode= new Node (val)
if (!this.root) this.root=newNode;
let current =this.root;
while (true) {
if(val === current.val) return undefined;
if(current.val<val){
if (current.right===null){
current.right=newNode;
return this
}
else
current=current.right}
if(current.val>val){
if (current.left===null){
current.left=newNode;
return this
}
else
current=current.left}
}
}
print (){
let all="Root=";
let visit=(current=this.root)=>{
if(!current.left && !current.right){
if(all[all.length-1]<current.val)
all+=`,LeR${current.val}`
else
all+=`,LeL${current.val}`
}
else{
if(all[all.length-1]<current.val)
all+=`,FR${current.val}`
else
all+=`,FL${current.val}`
}
if (current.left) visit(current.left)
if (current.right) visit(current.right)
}
visit()
all+=` ,valid bst:${this.isValidBST()}`
return all
}
isValidBST(node=this.root, min = null, max = null) {
if (!node) return true;
if (max !== null && node.data >= max) {
return false;
}
if (min !== null && node.data <= min) {
return false;
}
const leftSide = this.isValidBST(node.left, min, node.data);
const rightSide = this.isValidBST(node.right, node.val, max);
return leftSide && rightSide;
}
find(val){
let found=false
let innerFind=(current=this.root)=>{
if (val>current.val)
if (current.right != null) return innerFind(current.right)
if(val===current.val)
found= true
else
if (current.left != null)return innerFind(current.left)
if(val===current.val)
found= true
return found
}
return innerFind()
}
}
let tree=new Bst
tree.insert(8)
tree.insert(10)
tree.insert(13)
tree.insert(3)
tree.insert(1)
tree.insert(6)
tree.insert(4)
tree.insert(7)
tree.insert(2)
tree.print()
You can do this by technique is called recursion.
make an array with the structure ( left_subtree, key, right_subtree)
in your case
var array = [[3,5,6],8,[null,10,12]
class TreeNode {
constructor(key) {
this.key = key;
this.right = null;
this.left = null;
}
}
function parseTuple(data) {
if (data === null) {
let node = null;
return node;
}
else if (data.length === 3) {
let node = new TreeNode(data[1]);
node.left = parseTuple(data[0]);
node.right = parseTuple(data[2]);
return node;
}
else {
let node = new TreeNode(data);
return node;
}
}

Display Binary Search Tree Traversal on Javascript (Recursive way)

I am trying to console every data in a binary tree. My main problem is that I want to implementing in a recursive way. Basically I have this code so far:
this.levelOrder = function (root) {
if (root.data != null) {
console.log(root.data);
if (root.left != null) {
this.levelOrder(root.left);
}
if (root.right != null) {
this.levelOrder(root.right)
}
} else {
return;
}
};
The output is 3 2 1 5 4 7
But It should be 3 2 5 1 4 7. So Basically I am accessing the first child of the node instead of printing all the children first.
Assuming a tree like this,
4
2 6
1 3 5 7
and an object literal
tree = {
data: 4,
left: {
data: 2,
left: {
data: 1,
left: null,
right: null
},
right: {
data: 3,
left: null,
right: null
}
},
right: {
data: 6,
left: {
data: 5,
left: null,
right: null
},
right: {
data: 7,
left: null,
right: null
}
}
};
you could call the function recursively and get first the left part and then the right part of the tree. The algorithm is called depth-first search.
This function uses a single check, because this is sufficient to check first and then to move on.
var depthFirst = function (node) {
if (node) {
console.log(node.data);
depthFirst(node.left);
depthFirst(node.right)
}
},
tree = { data: 4, left: { data: 2, left: { data: 1, left: null, right: null }, right: { data: 3, left: null, right: null } }, right: { data: 6, left: { data: 5, left: null, right: null }, right: { data: 7, left: null, right: null } } };
depthFirst(tree); // 4 2 1 3 6 5 7
For a breadth-first search, an algorithm which is iterating every level of the tree first, you could use this code with the same tree data as above.
var breadthFirst = function (node) {
function bf(queue) {
var newQueue = [];
queue.forEach(function (node) {
console.log(node.data);
node.left && newQueue.push(node.left);
node.right && newQueue.push(node.right);
});
newQueue.length && bf(newQueue);
}
bf([node]);
},
tree = { data: 4, left: { data: 2, left: { data: 1, left: null, right: null }, right: { data: 3, left: null, right: null } }, right: { data: 6, left: { data: 5, left: null, right: null }, right: { data: 7, left: null, right: null } } };
breadthFirst(tree); // 4 2 6 1 3 5 7
Assuming the Node definition is like this:
export default class Node {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;
}
}
And the Tree definition is something like this:
import Node from './Node.js';
export default class BinarySearchTree {
constructor() {
this.root = null;
}
//methods such as search, traversal, insert, etc...
}
You can perform the traversals like this (preferably as methods of the BinarySearchTree) :
inorder(node) {
if (node !== null) {
this.inorder(node.left);
console.log(node.data);
this.inorder(node.right);
}
}
postorder(node) {
if (node !== null) {
this.postorder(node.left);
this.postorder(node.right);
console.log(node.data);
}
}
preorder(node) {
if (node !== null) {
console.log(node.data);
this.preorder(node.left);
this.preorder(node.right);
}
}
If you want to store the result of the traversal, you could pass down some object the methods would append the values to.
--
BONUS
If you want to display the whole tree in a somewhat pleasing manner, I'd suggest using some additional information stored in the nodes and in the state of your app.
First I'd add some additional info to each Node definition (x,y):
export default class Node {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;
this.x = 0;
this.y = 0;
}
}
Then create an empty grid (matrix) based on the current tree data,
createMatrix = (minX, maxX, depth) => {
let displayMatrix = [];
for (let i = 0; i <= depth; i++) {
let line = [];
for (let j = 0; j <= maxX - minX; j++) {
line.push(' ');
}
displayMatrix.push(line);
}
return displayMatrix;
}
Where minX is the smallest X value among all nodes, and maxX is the largest (getting these values could happen while inserting the nodes into the tree, or by traversing the tree and comparing node properties), and then fill it with the node data and the "\", "/" characters between nodes:
fillMatrix(node, matrix, minX, side = "root") {
if(node !== null){
matrix[node.y][node.x + Math.abs(minX)] = node.data;
if(side === "left"){
matrix[node.y-1][node.x + Math.abs(minX)+1] = '/';
}
else if (side === "right"){
matrix[node.y - 1][node.x + Math.abs(minX) - 1] = '\\';
}
this.fillMatrix(node.left, matrix, minX, "left");
this.fillMatrix(node.right, matrix, minX, "right");
}
return matrix;
}
Then this result matrix would hold a " " where there are no 'lines' and no values, hold the numbers where the node.data are, and hold "/" or "\" as the lines between the nodes.
Then you can use this to display it on a canvas or in the console.
For example, here I created a table dynamically, and in every element there is a div, which is formatted differently based on what kind of character it holds:
/* |
h
/ \
d l
/ \ / \
b f j n
/ \ / \ / \ / \
a c e g i k m o
bt.insert("d");
bt.insert("l");
bt.insert("b");
bt.insert("f");
bt.insert("j");
bt.insert("n");
bt.insert("a");
bt.insert("c");
bt.insert("e");
bt.insert("g");
bt.insert("i");
bt.insert("k");
bt.insert("m");
bt.insert("o");
*/
function printTree(inorder, bredthOrder, size) {
size = size || 2;
const total = inorder.length;
const numberOfLevels = Math.ceil(Math.log2(total));
let pointerBOrder = 0;
let blank = " ";
for (let level = 0; level < numberOfLevels; level++) {
let itemPerLevel = Math.pow(2, level);
let out = [];
let branches = [];
let slantDirection = true;//up
for (let itemNumber = 0; itemNumber < itemPerLevel; itemNumber++) {
let item = bredthOrder[pointerBOrder++];
for (let x = 1; x <= inorder.length; x++) {
const ino = inorder[x - 1];
let nInd = size * (x - 1);
if (item == ino) {
out[nInd] = item;
if (item) {
if (bredthOrder[0] == item) {
branches[nInd] = "|";
} else if (slantDirection) {
branches[nInd + 1] = "/";
} else {
if (nInd - 1 >= 0) {
branches[nInd - 1] = "\\";
}
}
slantDirection = !slantDirection;
}
} else {
out[nInd] = out[nInd] || blank;
}
branches[nInd] = branches[nInd] || blank;
for (let fill = 1; fill <= size - 1; fill++) {
out[nInd + fill] = blank;
branches[nInd + fill] = branches[nInd + fill] || blank;
}
}
}
console.log(branches.join(''));
console.log(out.join(''));
out = [];
}
}
class Node {
constructor(value) {
this.left = null;
this.right = null;
this.value = value;
}
}
class BinaryTree {
constructor(value) {
this.root = value == null ? null : new Node(value);
}
insert(value, node) {
if (node == null) {
node = this.root;
}
if (node == null) {
node = new Node(value);
return;
}
if (node.value >= value) {
if (node.left == null) {
node.left = new Node(value);
return;
} else {
this.insert(value, node.left);
return;
}
} else {
if (node.right == null) {
node.right = new Node(value);
return;
} else {
this.insert(value, node.right);
return;
}
}
}
print(node, acc) {
if (node == null) {
return acc;
} else {
if (node.left != null) {
acc = this.print(node.left, acc);
}
acc.push(node.value);
if (node.right != null) {
acc = this.print(node.right, acc);
}
return acc;
}
}
printInOrder() {
return this.print(this.root, []);
}
getSiblings(node) {
if (node == null) {
return [];
}
let acc = [];
if (node.left != null) {
acc.push(node.left);
}
if (node.right != null) {
acc.push(node.right);
}
return acc;
}
printBredthOrder() {
let result = [];
if (this.root == null) {
} else {
let acc = [this.root];
let workingAcc = [this.root];
let tmpAcc = [];
do {
tmpAcc = [];
for (let i = 0; i < workingAcc.length; i++) {
acc = [...acc, workingAcc[i].left];
acc = [...acc, workingAcc[i].right];
let left = this.getSiblings(workingAcc[i].left);
let right = this.getSiblings(workingAcc[i].right);
tmpAcc = [...tmpAcc, ...left];
tmpAcc = [...tmpAcc, ...right];
}
acc = [...acc, ...tmpAcc];
workingAcc = tmpAcc;
} while (tmpAcc.length != 0);
for (let i = 0; i < acc.length; i++) {
result.push(acc[i].value);
}
}
return result;
}
}
let bt = new BinaryTree("h");
bt.insert("d");
bt.insert("l");
bt.insert("b");
bt.insert("f");
bt.insert("j");
bt.insert("n");
bt.insert("a");
bt.insert("c");
bt.insert("e");
bt.insert("g");
bt.insert("i");
bt.insert("k");
bt.insert("m");
bt.insert("o");
let io = bt.printInOrder();
let bo = bt.printBredthOrder();
printTree(io, bo);
// Node Class
class Node {
constructor (val, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
}
// Binary Tree
const head = new Node(20,
new Node(10,
new Node(5,
new Node(3,
new Node(2,
new Node(1)),
new Node(4)),
new Node(8,
new Node(7,
new Node(6)),
new Node(9))),
new Node(15,
new Node(13,
new Node(11,
null,
new Node(12)),
new Node(14)),
new Node(18,
new Node(16,
null,
new Node(17)),
new Node(19)))),
new Node(30, new Node(25,
new Node(23,
new Node(22,
new Node(21)),
new Node(24)),
new Node(28,
new Node(27,
new Node(26)),
new Node(29))),
new Node(35,
new Node(33,
new Node(31,
null,
new Node(32)),
new Node(34)),
new Node(38,
new Node(36,
null,
new Node(37)),
new Node(39,
null,
new Node(40))))),
);
// VIEW
// 20
// 10 30
// 5 15 | 25 35
// 3 8 | 13 18 || 23 28 | 33 38
// 2 4 | 7 9 || 11 14 | 16 19 ||| 22 24 | 27 29 || 31 34 | 36 39
//1 n | n n || 6 n | n n ||| n 12 | n n || 17 n | n n |||| 21 n | n n || 26 n | n n |||| n 32 | n n || n 37 | n 40
// In Order Tree Traversal
const inOrder = (node) => {
if(node.left !== null) {
inOrder(node.left);
}
console.log(node.val);
if(node.right !== null) {
inOrder(node.right);
}
}
// Pre Order Tree Traversal
const preOrder = (node) => {
console.log(node.val);
if(node.left !== null) {
preOrder(node.left);
}
if(node.right !== null) {
preOrder(node.right);
}
}
// Post Order Tree Traversal
const postOrder = (node) => {
if(node.left !== null) {
postOrder(node.left);
}
if(node.right !== null) {
postOrder(node.right);
}
console.log(node.val);
}
// Node Count Recursively
const nodeCount = (node) => {
if(node.left !== null) {
nodeCount(node.left);
}
if(node.right !== null) {
nodeCount(node.right);
}
count++;
}
// Sum of all Nodes Recursively
const totalValue = (node) => {
if(node.left !== null) {
totalValue(node.left);
}
if(node.right !== null) {
totalValue(node.right);
}
total += node.val;
}
// inOrder(head);
// preOrder(head);
// postOrder(head);
let count = 0;
nodeCount(head)
console.log(count);
let total = 0;
totalValue(head)
console.log(total);
// NOTE
// if the values are continuous between 1/0 and n then the total is simply (n*(n+!))/2
// if the values are continuous between m and n then the total is simply ((n*(n+!))/2) - ((m*(m+!))/2)

Recursion to traverse all the nested child nodes of Binary Tree Javascript

I am playing around with a binary tree. I am trying to use recursion to find all the nested children's values and push all the values into an array. I started with the left tree to see if it works. I tried to call childrenArray() but the console says childrenArray() is not defined. When I ask if (typeof BinaryTree.prototype.childrenArray === 'function'), it returns true. Please teach me and tell me why I wasn't able to execute my code?
var Tree = function(value) {
if (!(this instanceof Tree)) {
return new Tree(value);
}
this.value = value;
this.children = [];
};
Tree.prototype.addChild = function(value) {
var child = new Tree(value);
this.children.push(child);
};
Tree.prototype.contains = function(value) {
if (this.value === value) {
return true;
} else {
for (var i = 0; i < this.children.length; i++) {
if (this.children[i] && this.children[i].contains(value)) {
return true;
}
}
return false;
}
};
var BinaryTree = function(value) {
if (!(this instanceof BinaryTree)) {
return new BinaryTree(value);
}
Tree.call(this, value);
};
BinaryTree.prototype = Object.create(Tree.prototype);
BinaryTree.prototype.addChild = function(value) {
if (value < this.value) {
if (this.children[0] === undefined) {
this.children[0] = new BinaryTree(value);
}
this.children[0].addChild(value);
} else if (value > this.value) {
if (this.children[1] === undefined) {
this.children[1] = new BinaryTree(value);
}
this.children[1].addChild(value);
}
};
BinaryTree.prototype.contains = function(value) {
if (value < this.value) {
if (this.children[0] === undefined) {
return false;
}
return this.children[0].contains(value);
} else if (value > this.value) {
if (this.children[1] === undefined) {
return false;
}
return this.children[1].contains(value);
}
};
var a = new BinaryTree();
a.value = 10;
a.addChild(4);
a.addChild(11);
a.addChild(3);
BinaryTree.prototype.childrenArray = function() {
var results = [];
if (this.value) {
results.push(this.value);
}
if (this.children[0].length === 0) {
return results;
}
for (var i = 0; i < this.children[0].children.length; i++) {
if (this.children[i].value) {
results.push(this.children[i].value);
return this.childrenArray();
}
}
};
a.childrenArray();
As #melpomene mentioned, you are invoking childArray but you didn't define it anywhere. I assume the line return childArray(); ended up there by mistake, you probably meant to recursively return childrenArray for the left child of root.
I would like to mention that your loop itself (without the recursive call):
for(var i = 0; i < this.children[0].children.length; i++) { // <-- you are iterating over the children of root's left child
if(this.children[i].value) { // <-- but you are accessing root's current child
results.push(this.children[i].value);
}
}
is somewhat confusing. You are iterating over the children of root's left child children[0].children, but on each iteration you check if the root's children themselves children[i] have a value and you push that value.
This is incorrect and will break if, for example, the root only has a left child that has both children (i will be out of bounds).
Here's how you can approach this problem. The recursion to print the left tree in your case can be broken down into the following cases:
If the current node has no value, return an empty array
Else If the current node has no children, return an array only containing the current value
Else if the current node has a left child, run childrenArray on it recursively and add the results to the results array
Here's how that would look:
BinaryTree.prototype.childrenArray = function() {
var results = [];
// case 1:
if (this.value === undefined) {
return results;
}
// case 2:
results.push(this.value);
if (this.children.length === 0) {
return results;
}
// case 3:
var leftChild = this.children[0];
if (leftChild) {
results = results.concat(leftChild.childrenArray());
}
/* add code here for the right child to complete your function */
return results;
};
Full Example:
var BinaryTree = function(value) {
this.value = value;
this.children = [];
};
BinaryTree.prototype.addChild = function (value) {
if (value < this.value) {
if (this.children[0] === undefined) {
this.children[0] = new BinaryTree(value);
}
this.children[0].addChild(value);
} else if (value > this.value) {
if (this.children[1] === undefined) {
this.children[1] = new BinaryTree(value);
}
this.children[1].addChild(value);
}
};
BinaryTree.prototype.childrenArray = function() {
var results = [];
// case 1:
if (this.value === undefined) {
return results;
}
// case 2:
results.push(this.value);
if (this.children.length === 0) {
return results;
}
// case 3:
var leftChild = this.children[0];
if (leftChild) {
results = results.concat(leftChild.childrenArray());
}
/* add code here for the right child to complete your function */
return results;
};
var a = new BinaryTree(10);
a.addChild(4);
a.addChild(11);
a.addChild(3);
console.log(a.childrenArray()); // [10, 4, 3]
Last thing, based on your insertion logic (insert left if smaller, right if larger), you are building a Binary Search Tree not a Binary Tree. Binary tree's don't follow any particular insertion logic. Take a look at this question for clarification

Categories