Finding shortest path unweighted graph using BFS (javascript) - javascript

I'm trying to apply BFS to find the length of the shortest path in a graph, but am not quite getting the right result.
I try to find the shortest path by visiting each node in the graph; then mark the ones that are visited, and continue recording the length of the path. What I hope to return is an array that contains the shortest path, but I think I am doing something wrong in the process.
I think this has something to do with how I am indexing my arrays and recording my distances.
My input is currently formatted in the form of an array that contains the neighbors for each vertex i. So, for instance, graph[i] would give you an array of neighbors of vertex i.
Any thoughts on how I can go about fixing my issue would be very helpful. Thanks!
var shortestPathLength = function(graph) {
let distances = []
let pq = []
distances[0] = 0
let mygraph = {}
for (var i = 0; i<graph.length; i++) {
distances[i] = -1
mygraph[i] = graph[i]
}
pq.push(mygraph[0])
while(pq.length > 0) {
let min_node = pq.shift()
for(var i = 0; i<min_node.length; i++) {
candidate = distances[i] + 1
if(distances[min_node[i]]== -1) {
distances[min_node[i]] = distances[i] + 1
pq.push(graph[min_node[i]])
}
else if (candidate < distances[min_node[i]]) {
distances[min_node[i]] = distances[i] + 1
}
}
}
function getSum(total, num) {
return total + num;
}
console.log(distances)
return distances.length
};

Your problem is candidate = distances[i] + 1. The i is the index of the edge inside the min_node, which isn't interesting at all. What you are looking for is the current distance to the min_node. You will need to either assign the distance as a property of the min_node object itself, or you will need to store the id (index in graph) of the nodes in your queue instead of the object itself.
I've made a few other simplifications, but the only showstopper problem in your code was the distance lookup.
function shortestPathLength = function(graph) {
const distances = Array(graph.length).fill(-1);
distances[0] = 0; // start node
const queue = [0];
while (queue.length > 0) {
const node_index = queue.shift();
// ^^^^^
const edges = graph[node_index]; // get the node itself
const candidate = distances[node_index] + 1; // outside of the loop
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (const target in edges) {
if (distances[target] == -1) {
distances[target] = candidate;
queue.push(target); // not graph[target]
// ^^^^^^
}
}
}
return distances;
}

Related

My Depth First Search function seems not to return list of visited nodes i need to Visualize on front-end

I am writing my own visualizer based on this tutorial here https://github.com/clementmihailescu/Pathfinding-Visualizer-Tutorial,
I created a depth-first algorithm based on the javascript example in the Oreilly Book, data structures, and algorithms, example below here
function dfs(v) {
this.marked[v] = true;
// if statement for print is not required
if (this.adj[v] != undefined)
print("Visited vertex: " + v);
for each(var w in this.adj[v]) {
if (!this.marked[w]) {
this.dfs(w);
}
}
}
Below is my implementation of the algorithm in my own react app is below here, the grid parameter is simply a 2-dimensional array of nodes, the closestNode being the start node to begin from, and the finishNode is the node to cancel the operation once reached, visitedNodesInOrder is an array to kept track of nodes being visited
export function startDfs(grid, closestNode, finishNode, visitedNodesInOrder) {
// console.log(closestNode);
closestNode.isVisited = true;
if (closestNode !== undefined) {
visitedNodesInOrder.push(closestNode);
}
var unvisitedNeighbors = getUnvisitedNeighbors(closestNode, grid);
for (var x = 0; x < unvisitedNeighbors.length; x++) {
if (!unvisitedNeighbors[x].isVisited) {
if (unvisitedNeighbors[x].isWall) {
continue;
}
if (unvisitedNeighbors[x] === finishNode) {
return visitedNodesInOrder; // RETURN VISITED NODES HERE
}
startDfs(grid, unvisitedNeighbors[x], finishNode, visitedNodesInOrder);
}
}
}
function getUnvisitedNeighbors(node, grid) {
const neighbors = [];
const {col, row} = node;
if (row > 0) neighbors.push(grid[row - 1][col]);
if (row < grid.length - 1) neighbors.push(grid[row + 1][col]);
if (col > 0) neighbors.push(grid[row][col - 1]);
if (col < grid[0].length - 1) neighbors.push(grid[row][col + 1]);
return neighbors.filter(neighbor => !neighbor.isVisited);
}
My final intention is to collect the visitedNodesInOrder array and animate the list of nodes on the grid i created on the front end, the 2D array is an exact reflection of the grid i made on the front end using a map function to map the original 2D List with nodes to an HTML, CSS DOM layout, my final implementation that's giving me an error is below
visualizeDijkstra() {
var nodesInShortestPathOrder = [];
var visitedNodesInOrder = [];
const {grid} = this.state;
const startNode = grid[START_NODE_ROW][START_NODE_COL];
const finishNode = grid[FINISH_NODE_ROW][FINISH_NODE_COL];
var start = new Date().getTime();
visitedNodesInOrder = startDfs(
grid,
startNode,
finishNode,
visitedNodesInOrder,
);
console.log(visitedNodesInOrder);
boardToClear = visitedNodesInOrder;
//nodesInShortestPathOrder = getNodesInShulortestPathOrder(finishNode);
var stop = new Date().getTime();
elasped = stop - start;
this.animateDijkstra2(visitedNodesInOrder);
}
this is the error i am getting below, i don't understand what happened to the array
PathfindingVisualizer.jsx:113 Uncaught TypeError: Cannot read properties of undefined (reading 'length')
at PathfindingVisualizer.animateDijkstra2

Parsing loops in a javascript interpreter

I've been exploring writing a very basic / limited interpreter in javascript as an exercise. All has been going well until I introduced the concept of LOOPs.
Given the following script:
LOOP 2
A
LOOP 3
B
END
LOOP 4
C
LOOP 5
D
END
E
END
F
END
The algorithm should visit the inner tokens in the following sequence:
ABBBCDDDDDECDDDDDECDDDDDECDDDDDEFABBBCDDDDDECDDDDDECDDDDDECDDDDDEF
The following does the trick, but it requires lots of iterating over the tokens. It's an improvement over a previous slicing approach I used that manually expanded the loops, but is far from optimal.
/**
* In practice, we'll grab each token as we read the script,
* but to keep this simple and focus on the loop algorithm,
* we can cheat and make an array of all the tokens.
*/
const getTokens = (s) => s.replace(/[\W_]+/g, " ").split(" ").filter(Boolean);
/* Temp vars - ideally, I'd like to solve this with arrays. */
const start = []; // Loop start indices
const end = []; // Loop end indices
const counts = []; // Times to loop
const completed = []; // Loops completed
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (token === "LOOP") {
if (start.length == 0 || i > start[start.length - 1]) {
// Add new loop index if we haven't seen it before
start.push(i); // Store the loop index
counts.push(Number(tokens[i + 1])); // The loop count is always next LOOP token
completed.push(0); // Initialize with 0 completed at index
// Find the end index for the loop
// Note: This is the slowest part.
let skip = 0;
for (let j = i + 2; j < tokens.length; j++) {
if (tokens[j] == "LOOP") {
skip++; // Increase nest depth
} else if (tokens[j] == "END") {
if (skip == 0) {
end.push(j); // Found matching loop close
break;
}
skip--;
}
}
}
i++; // Skip over the loop count
continue;
} else if (token === "END") {
let j;
for (j = 0; j < end.length; j++) {
if (end[j] == i) break; // Found matching end index
}
const isCompleted = completed[j] == counts[j] - 1;
if (!isCompleted) {
i = start[j] + 1;
completed[j]++;
for (let k = j + 1; k < start.length; k++) {
completed[k] = 0; // Reset nested loops in between
}
}
continue;
}
console.log(tokens[i]);
}
https://jsfiddle.net/5wpa8t4n/
What's a better way to accomplish this array-based approach using a single pass through the script, or at worst 2 passes, but not N-LOOP passes?
You don't need to know the position of the matching end of the loop when starting to interpret it. All you need to record is the position to jump back to when encountering the next end, but until then just continue interpreting token by token.
These positions, together with the respective counters, can be stored in a stack structure.
const script = `
DO
A
DO
B
LOOP 3
DO
C
DO
D
LOOP 5
E
LOOP 4
F
LOOP 2
`
const parse = (script) =>
script
.replace(/[\W_]+/g, " ")
.split(" ")
.filter(Boolean);
const interpret = (code) => {
let loops = []; // Active loops: iteration count and jump target
let ip = 0; // instruction pointer
let result = "";
while (ip < code.length) {
const instruction = code[ip];
switch (instruction) {
case "DO": {
++ip;
loops.push({count: 0, start: ip});
} break;
case "LOOP": {
const limit = Number(code[++ip]);
const {count, start} = loops.pop();
if (count < limit) {
loops.push({count: count+1, start});
ip = start; // jump back
} else {
++ip;
}
} break;
default: {
++ip;
result += instruction; // a print statement
} break;
}
}
return result;
};
console.log(interpret(parse(script)));
I've simplified the structure a bit to use do-while loops, so I'd never have to skip the loop body. In a true byte code, emitted by a parser, the jump targets (both back and forth) would be part of the instructions themselves, and only the count "variables" would need to be stored in the stack. The jump targets never change so you'd need to generate them only once in the parse function.

Find path between 2 countries with borders

I Have a JSON with countries from a continent. Each country has a array that has it's border countries. Given 2 countries from the same continent, return it's route. E.g.:
Brazil and USA -> 1. Colômbia, 2. Panamá, 3. Costa Rica, 4. Nicarágua, 5. Honduras,
6. Guatemala, 7. México.
I'm struggling to organize it on one array only to make a BFS (Breadth First Search). What i have until now is:
function traverse(main_key, obj) {
obj.forEach(function(key) {
if (visited[main_key] === undefined) {
visited[main_key] = new Array()
}
visited[main_key].push(obj)
if (typeof(borders[key]) === 'array' && visited[main_key].indeOf(key) !== -1) {
traverse(borders[key])
}
else {
//do something with the actual value
console.log(main_key, borders[key], key)
}
})
};
traverse('BRA', borders['BRA'])
Here is JSON sample: https://restcountries.eu/rest/v2/region/americas
I think my main problem is i'm struggling with transforming this JSON into a GRAPH.
Checkout Dijkstra's algorithm where you can assume that countries as nodes in the algorithm and bordering countries have a distance of 1 and non-bordering countries don't have borders.
Edit:
Here is an implementation using the data you specified. Pass the data you get from the JSON sample (entire array) as the graph parameter, source country (for example 'BRA'), target country (for example 'USA'). I didn't do a lot of testing, so I can't guarantee this is bug free.
function dijkstra(graph, source, target) {
var dist = {};
var prev = {};
var vertices = [];
for (var i = 0; i < graph.length; i++) {
var vertex = graph[i];
dist[vertex.alpha3Code] = Infinity;
prev[vertex.alpha3Code] = null;
vertices.push(vertex);
}
dist[source] = 0;
while (vertices.length > 0) {
// Find the vertex having smallest dist.
var u = vertices.reduce(function(p, current) {return !p || dist[current.alpha3Code] < dist[p.alpha3Code] ? current : p;}, null);
const index = vertices.indexOf(u);
vertices.splice(index, 1);
if (u.borders && Array.isArray(u.borders)) {
for (var i = 0; i < u.borders.length; i++) {
var v = u.borders[i];
const alt = dist[u.alpha3Code] + 1;
if (alt < dist[v]) {
dist[v] = alt;
prev[v] = u;
}
}
}
}
var result = [];
while (target) {
result.splice(0, 0, target);
target = prev[target] ? prev[target].alpha3Code : null;
}
// Any empty array return means there is no path. For example one of the countries is an island.
if (result.length === 1) {
return [];
}
return result;
}

How do I implement this "counter" on a Hash Table in JavaScript?

I'm creating this Hash Table in JavaScript and I want it warn me each time I'm adding existing values in the array. I tried my own implementation, but it isn't working.
function hashFunction(s,tableSize){
let hash = 17;
for(let i = 0; i<s.length; i++){
hash = (13*hash * s.charCodeAt(i)) % tableSize;
}
return hash;
};
class HashTable {
table = new Array(2000);
numItems = 0;
loadFactor = this.numItems/this.table.length;
setItem = (key,value)=>{
const idx = hashFunction(key, this.table.length);
if(this.table.includes(key)){
return "already exists"
}else this.numItems++
if(this.table[idx]){
this.table[idx].push([key,value])
}else this.table[idx]=[[key,value]];
};
getItem = (key)=>{
const idx = hashFunction(key,this.table.length);
if(!this.table[idx]){
return "Key doesn't exist";
}else
return this.table[idx].find(x=>x[0]===key)[1];
};
};
let myHash = new HashTable
myHash.setItem("first","daniel")
myHash.setItem("last","esposito")
myHash.setItem("age","21")
myHash.setItem("height","1,90")
It would be beneficial to review implementing pseudo code and existing implementations. There were numerous errors which can summarized as the original implementation failing to correctly handle pairs within a bucket.
Here is a working update which salvages some of the structure while discarding most of the inconsistent and/or incorrect implementation - I've left comments as reference points to understand why the original was incorrect. The code has been structured to illustrate access/creation of buckets, access/creation of pairs within the buckets, and behavior selection depending on the case.
YMMV.
function hashFunction(s, tableSize){
let hash = 17;
for (let i = 0; i < s.length; i++){
hash = (13 * hash + s.charCodeAt(i)) % tableSize;
// ^-- Ry suggests the original * is a typo.
}
return hash;
};
class HashTable {
// I've eliminated 'load factor' to cover a basic implementation.
// See rebalancing, good table size selection, alternative collision
// resolution strategies, etc.
// This implementation might pass a CS101 course.
// Yes, for THIS EXAMPLE the TABLE SIZE IS CHOSEN AS 2.
// This ensures that there are multiple items per bucket
// which guarantees the collision resolution paths are invoked.
// This is a terrible choice in practice.
table = new Array(2);
numItems = 0;
setItem = (key, value)=>{
const idx = hashFunction(key, this.table.length);
// set/get should ONLY access simple properties or
// a BUCKET from the hash code as top-level structures.
// (Using table.includes(..) here is fundamentally INCORRECT.)
let bucket = this.table[idx];
if (bucket) {
// Existing bucket. Search in HERE to see if the key exists.
// This should generally be the same code as the "get".
let pair = bucket.find(x => x[0] === key);
if (pair) {
// Same pair, update value.
pair[1] = value;
return false; // "existing"
} else {
// Add new pair to bucket.
bucket.push([key, value]);
this.numItems += 1;
return true; // "new"
}
} else {
// Create a new bucket and item pair.
this.table[idx] = [[key, value]];
this.numItems += 1;
return true; // "new"
}
};
getItem = (key) =>{
const idx = hashFunction(key, this.table.length);
// Code should match close to 'set'
let bucket = this.table[idx];
if (bucket) {
let pair = bucket.find(x => x[0] === key);
if (pair) {
// Bucket exists and key exists within bucket.
return pair[1];
}
}
// The result should be the same if the key doesn't exist because
// bucket is not found, or if the bucket is found and the
// key does not exist within the bucket..
return undefined;
};
}
let myHash = new HashTable
var items = [
["first", "daniel"],
["last", "esposito"],
["age", 21],
["height", 1.9]
]
// Insert multiple values:
// Check to see inserted report true/not,
// and that the numItems is increased appropriately.
for (let run of [1, 2]) {
for (let i of items) {
let [k, v] = i;
var inserted = myHash.setItem(k, v);
var found = myHash.getItem(k) === v;
console.log(`run=${run} key=${k} value=${v} inserted=${inserted} numItems=${myHash.numItems} found=${found}` )
}
}
Output:
run=1 key=first value=daniel inserted=true numItems=1 found=true
run=1 key=last value=esposito inserted=true numItems=2 found=true
run=1 key=age value=21 inserted=true numItems=3 found=true
run=1 key=height value=1.9 inserted=true numItems=4 found=true
run=2 key=first value=daniel inserted=false numItems=4 found=true
run=2 key=last value=esposito inserted=false numItems=4 found=true
run=2 key=age value=21 inserted=false numItems=4 found=true
run=2 key=height value=1.9 inserted=false numItems=4 found=true
hash += (13*hash * s.charCodeAt(i)) % tableSize;

Manipulate more javascript array based on another array

I've a strange thing to do but I don't know how to start
I start with this vars
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
So to start all the 3 array have the same length and the very first operation is to see if there is a duplicate value in sky array, in this case the 0 is duplicated and only in this case is at the end, but all of time the sky array is sorted. So I've to remove all the duplicate (in this case 0) from sky and remove the corresponding items from base and sum the corresponding items on ite. So if there's duplicate on position 4,5 I've to manipulate this conditions. But let see the new 3 array:
var new_base = [1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var new_sky = [0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var new_ite = [139,38,13,15,6,4,6,3,2,1,2,1,1,1];
If you see the new_ite have 139 instead the 64,52,23, that is the sum of 64+52+23, because the first 3 items on sky are the same (0) so I remove two corresponding value from base and sky too and I sum the corresponding value into the new_ite array.
There's a fast way to do that? I thought a for loops but I stuck at the very first for (i = 0; i < sky.length; i++) lol, cuz I've no idea on how to manipulate those 3 array in that way
J
When removing elements from an array during a loop, the trick is to start at the end and move to the front. It makes many things easier.
for( var i = sky.length-1; i>=0; i--) {
if (sky[i] == prev) {
// Remove previous index from base, sky
// See http://stackoverflow.com/questions/5767325/how-to-remove-a-particular-element-from-an-array-in-javascript
base.splice(i+1, 1);
sky.splice(i+1, 1);
// Do sum, then remove
ite[i] += ite[i+1];
ite.splice(i+1, 1);
}
prev = sky[i];
}
I won't speak to whether this is the "fastest", but it does work, and it's "fast" in terms of requiring little programmer time to write and understand. (Which is often the most important kind of fast.)
I would suggest this solution where j is used as index for the new arrays, and i for the original arrays:
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
var new_base = [], new_sky = [], new_ite = [];
var j = -1;
sky.forEach(function (sk, i) {
if (!i || sk !== sky[i-1]) {
new_ite[++j] = 0;
new_base[j] = base[i];
new_sky[j] = sk;
}
new_ite[j] += ite[i];
});
console.log('new_base = ' + new_base);
console.log('new_sky = ' + new_sky);
console.log('new_ite = ' + new_ite);
You can use Array#reduce to create new arrays from the originals according to the rules:
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
var result = sky.reduce(function(r, n, i) {
var last = r.sky.length - 1;
if(n === r.sky[last]) {
r.ite[last] += ite[i];
} else {
r.base.push(base[i]);
r.sky.push(n);
r.ite.push(ite[i]);
}
return r;
}, { base: [], sky: [], ite: [] });
console.log('new base:', result.base.join(','));
console.log('new sky:', result.sky.join(','));
console.log('new ite:', result.ite.join(','));
atltag's answer is fastest. Please see:
https://repl.it/FBpo/5
Just with a single .reduce() in O(n) time you can do as follows; (I have used array destructuring at the assignment part. One might choose to use three .push()s though)
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330],
sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17],
ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1],
results = sky.reduce((r,c,i) => c === r[1][r[1].length-1] ? (r[2][r[2].length-1] += ite[i],r)
: ([r[0][r[0].length],r[1][r[1].length],r[2][r[2].length]] = [base[i],c,ite[i]],r),[[],[],[]]);
console.log(JSON.stringify(results));

Categories

Resources