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)
Related
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>
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}));
This is my implementation of a graph, to get the shortest path between A and B.
class Queue {
constructor() {
this.head = null;
this.tail = null;
this.size = 0;
}
offer(item) {
const p = new QueueNode(item);
this.size++;
if (this.head === null) {
this.head = p;
this.tail = p;
return;
}
this.tail.next = p;
this.tail = p;
}
poll() {
if (this.size === 0) {
throw TypeError("Can't deque off an empty queue.");
}
this.size--;
const item = this.head;
this.head = this.head.next;
return item.val;
}
peek() {
if (this.size === 0) {
throw TypeError("Empty Queue.")
}
return this.head.val;
}
isEmpty() {
return this.head === null;
}
}
class QueueNode {
constructor(item) {
this.val = item;
this.next = null;
}
}
class Graph {
constructor(directed = false) {
this.numVertices = 0;
this.directed = directed;
this.dict = {}
}
addEdge(v1, v2, weight = 1) {
let p, q;
if (v1 in this.dict) {
p = this.dict[v1];
} else {
p = new GraphNode(v1);
this.dict[v1] = p;
this.numVertices++;
}
if (v2 in this.dict) {
q = this.dict[v2];
} else {
q = new GraphNode(v2);
this.dict[v2] = q;
this.numVertices++;
}
p.addEdge(q);
if (!this.directed) {
q.addEdge(p);
}
}
stringify() {
for (const [key, value] of Object.entries(this.dict)) {
console.log(`${key}: ${[...value.adjacencySet].map(x => x.data)}`);
}
}
buildDistanceTable(source) {
let p;
if (this.dict[source] === undefined) {
throw TypeError('Vertex not present in graph')
} else {
p = this.dict[source];
}
const distanceTable = {};
for (const [key, value] of Object.entries(this.dict)) {
distanceTable[key] = [-1, -1];
}
distanceTable[p.data] = [0, p.data];
const queue = new Queue();
queue.offer(p);
while (!queue.isEmpty()) {
let curr = queue.poll();
let curr_distance = distanceTable[curr.data][0];
curr.adjacencySet.forEach((item) => {
if (distanceTable[item.data] === -1) {
distanceTable[item.data] = [1 + curr_distance, curr.data];
console.log(distanceTable);
if (item.adjacencySet.length > 0) {
queue.offer(item);
}
}
})
}
return distanceTable;
}
shortestPath(source, destination) {
const distanceTable = this.buildDistanceTable(source);
const path = [destination];
let prev = distanceTable[destination][1];
while (prev !== -1 && prev !== source) {
path.unshift(prev);
prev = distanceTable[prev][1];
}
if (prev === null) {
console.log("There's no path from source to destination");
} else {
path.unshift(source);
path.map(item => {
console.log(item);
});
}
}
}
class GraphNode {
constructor(data) {
this.data = data;
this.adjacencySet = new Set();
}
addEdge(node) {
this.adjacencySet.add(node)
}
}
graph = new Graph(directed = false);
graph.addEdge(0, 1);
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(2, 3);
graph.addEdge(1, 4);
graph.addEdge(3, 5);
graph.addEdge(5, 4);
graph.addEdge(3, 6);
graph.addEdge(6, 7);
graph.addEdge(0, 7);
graph.stringify();
graph.shortestPath(1, 7);
When I run this it give 1, 7 however that's not the shortest path. What am I doing wrong here.
You have 2 issue in your code (that sabotage the building of the distance table):
You missing index in: if (distanceTable[item.data] === -1) { -> each item in the distance table is of array therefor it need to be: if (distanceTable[item.data][0] === -1) {
Set size in node js checked with size and not length (as in documentation) therefor item.adjacencySet.length is always undefined so you need to change: if (item.adjacencySet.length> 0) { to if (item.adjacencySet.size > 0) {
After those 2 changes your code return me path of 1 -> 0 -> 7
Just small side issue: you missing some ; and "new" before throwing TypeError...
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;
}
}
There's a binary search tree, where all nodes to the left are smaller, and all nodes to the right are greater.
I'm trying to create a function isPresent that will check if value is present in the BST. If yes, it returns 1. If no, it returns 0.
this.isPresent = function(root, val) {
if (val == root.data) {
return 1;
}
if (val < root.data) {
if (root.left) {
this.isPresent(root.left, val);
} else {
return 0;
}
} else {
if (root.right) {
this.isPresent(root.right, val);
} else {
return 0;
}
}
}
Entire code:
function Node(data) {
this.data = data;
this.left = null;
this.right = null;
};
function BinarySearchTree() {
this.insert = function(root, data) {
if (root === null) {
this.root = new Node(data);
return this.root;
}
if (data < root.data) {
if (root.left) {
this.insert(root.left, data);
} else {
root.left = new Node(data);
}
} else {
if (root.right) {
this.insert(root.right, data);
} else {
root.right = new Node(data);
}
}
return this.root;
}
this.isPresent = function(root, val) {
if (val == root.data) {
return 1;
}
if (val < root.data) {
if (root.left) {
this.isPresent(root.left, val);
} else {
return 0;
}
} else {
if (root.right) {
this.isPresent(root.right, val);
} else {
return 0;
}
}
}
}
How input is formatted:
process.stdin.resume();
process.stdin.setEncoding('ascii');
var _input = "";
process.stdin.on('data', function(data) {
_input += data;
});
process.stdin.on('end', function() {
var tree = new BinarySearchTree();
var root = null;
var values = _input.spit('\n').map(Number);
var j = 1;
for (var i = 0; i < values[0]; i++) {
root = tree.insert(root, values[j]);
j++;
}
var k = j + 1;
for (var i = 0; i < values[j]; i++) {
process.stdout.write(tree.isPresent(root, values[k]) + '\n');
k++;
}
})
Sample input:
11 <--- this line indicating next 11 lines are values to be inserted to BST
20
10
30
8
12
25
40
6
11
13
23
4 <--- this line indicated the next 4 lines to be tested whether they are present in BST
30
10
12
15