Skip to content

Commit 90eac72

Browse files
committed
Review changes
1 parent a9e17ed commit 90eac72

File tree

1 file changed

+140
-93
lines changed

1 file changed

+140
-93
lines changed

Diff for: Graphs/Astar.js

+140-93
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,154 @@
11
/**
2-
* Author: Mathang Peddi
3-
* A* Search Algorithm implementation in JavaScript
2+
* @author Mathang Peddi
43
* A* Algorithm calculates the minimum cost path between two nodes.
54
* It is used to find the shortest path using heuristics.
65
* It uses graph data structure.
76
*/
87

9-
function createGraph(V, E) {
10-
// V - Number of vertices in graph
11-
// E - Number of edges in graph (u,v,w)
12-
const adjList = [] // Adjacency list
13-
for (let i = 0; i < V; i++) {
14-
adjList.push([])
8+
// Euclidean distance heuristic for 2D points
9+
function euclideanHeuristic(pointA, pointB) {
10+
const dx = pointA[0] - pointB[0];
11+
const dy = pointA[1] - pointB[1];
12+
return Math.sqrt(dx * dx + dy * dy);
1513
}
16-
for (let i = 0; i < E.length; i++) {
17-
adjList[E[i][0]].push([E[i][1], E[i][2]])
18-
adjList[E[i][1]].push([E[i][0], E[i][2]])
19-
}
20-
return adjList
21-
}
22-
23-
// Heuristic function to estimate the cost to reach the goal
24-
// You can modify this based on your specific problem, for now, we're using Manhattan distance
25-
function heuristic(a, b) {
26-
return Math.abs(a - b)
27-
}
28-
29-
function aStar(graph, V, src, target) {
30-
const openSet = new Set([src]) // Nodes to explore
31-
const cameFrom = Array(V).fill(-1) // Keep track of path
32-
const gScore = Array(V).fill(Infinity) // Actual cost from start to a node
33-
gScore[src] = 0
34-
35-
const fScore = Array(V).fill(Infinity) // Estimated cost from start to goal (g + h)
36-
fScore[src] = heuristic(src, target)
37-
38-
while (openSet.size > 0) {
39-
// Get the node in openSet with the lowest fScore
40-
let current = -1
41-
openSet.forEach((node) => {
42-
if (current === -1 || fScore[node] < fScore[current]) {
43-
current = node
14+
15+
// Priority Queue (Min-Heap) implementation
16+
class PriorityQueue {
17+
constructor() {
18+
this.elements = [];
19+
}
20+
21+
enqueue(node, priority) {
22+
this.elements.push({ node, priority });
23+
this.bubbleUp();
24+
}
25+
26+
bubbleUp() {
27+
let index = this.elements.length - 1;
28+
while (index > 0) {
29+
let parentIndex = Math.floor((index - 1) / 2);
30+
if (this.elements[index].priority >= this.elements[parentIndex].priority) break;
31+
[this.elements[index], this.elements[parentIndex]] = [this.elements[parentIndex], this.elements[index]];
32+
index = parentIndex;
4433
}
45-
})
46-
47-
// If the current node is the target, reconstruct the path and return
48-
if (current === target) {
49-
const path = []
50-
while (cameFrom[current] !== -1) {
51-
path.push(current)
52-
current = cameFrom[current]
34+
}
35+
36+
dequeue() {
37+
if (this.elements.length === 1) {
38+
return this.elements.pop().node;
5339
}
54-
path.push(src)
55-
return path.reverse()
40+
41+
const node = this.elements[0].node;
42+
this.elements[0] = this.elements.pop();
43+
this.sinkDown(0);
44+
return node;
5645
}
57-
58-
openSet.delete(current)
59-
60-
// Explore neighbors
61-
for (let i = 0; i < graph[current].length; i++) {
62-
const neighbor = graph[current][i][0]
63-
const tentative_gScore = gScore[current] + graph[current][i][1]
64-
65-
if (tentative_gScore < gScore[neighbor]) {
66-
cameFrom[neighbor] = current
67-
gScore[neighbor] = tentative_gScore
68-
fScore[neighbor] = gScore[neighbor] + heuristic(neighbor, target)
69-
70-
if (!openSet.has(neighbor)) {
71-
openSet.add(neighbor)
46+
47+
sinkDown(index) {
48+
const length = this.elements.length;
49+
const element = this.elements[index];
50+
while (true) {
51+
let leftChildIndex = 2 * index + 1;
52+
let rightChildIndex = 2 * index + 2;
53+
let swapIndex = null;
54+
55+
if (leftChildIndex < length && this.elements[leftChildIndex].priority < element.priority) {
56+
swapIndex = leftChildIndex;
57+
}
58+
59+
if (rightChildIndex < length && this.elements[rightChildIndex].priority < (swapIndex === null ? element.priority : this.elements[leftChildIndex].priority)) {
60+
swapIndex = rightChildIndex;
7261
}
62+
63+
if (swapIndex === null) break;
64+
65+
[this.elements[index], this.elements[swapIndex]] = [this.elements[swapIndex], this.elements[index]];
66+
index = swapIndex;
7367
}
7468
}
69+
70+
isEmpty() {
71+
return this.elements.length === 0;
72+
}
7573
}
76-
77-
return [] // Return empty path if there's no path to the target
78-
}
79-
80-
module.exports = { createGraph, aStar }
81-
82-
// const V = 9
83-
// const E = [
84-
// [0, 1, 4],
85-
// [0, 7, 8],
86-
// [1, 7, 11],
87-
// [1, 2, 8],
88-
// [7, 8, 7],
89-
// [6, 7, 1],
90-
// [2, 8, 2],
91-
// [6, 8, 6],
92-
// [5, 6, 2],
93-
// [2, 5, 4],
94-
// [2, 3, 7],
95-
// [3, 5, 14],
96-
// [3, 4, 9],
97-
// [4, 5, 10]
98-
// ]
99-
100-
// const graph = createGraph(V, E)
101-
// const path = aStar(graph, V, 0, 4) // Find path from node 0 to node 4
102-
// console.log(path)
103-
104-
/**
105-
* The function returns the optimal path from the source to the target node.
106-
* The heuristic used is Manhattan distance but it can be modified.
107-
*/
74+
75+
function aStar(graph, src, target, points) {
76+
const openSet = new PriorityQueue(); // Priority queue to explore nodes
77+
openSet.enqueue(src, 0);
78+
79+
const cameFrom = Array(graph.length).fill(null); // Keep track of path
80+
const gScore = Array(graph.length).fill(Infinity); // Actual cost from start to a node
81+
gScore[src] = 0;
82+
83+
const fScore = Array(graph.length).fill(Infinity); // Estimated cost from start to goal (g + h)
84+
fScore[src] = euclideanHeuristic(points[src], points[target]);
85+
86+
while (!openSet.isEmpty()) {
87+
// Get the node in openSet with the lowest fScore
88+
const current = openSet.dequeue();
89+
90+
// If the current node is the target, reconstruct the path and return
91+
if (current === target) {
92+
const path = [];
93+
while (cameFrom[current] !== -1) {
94+
path.push(current);
95+
current = cameFrom[current];
96+
}
97+
path.push(src);
98+
return path.reverse();
99+
}
100+
101+
// Explore neighbors using destructuring for cleaner code
102+
for (const [neighbor, weight] of graph[current]) {
103+
const tentative_gScore = gScore[current] + weight;
104+
105+
if (tentative_gScore < gScore[neighbor]) {
106+
cameFrom[neighbor] = current;
107+
gScore[neighbor] = tentative_gScore;
108+
const priority = gScore[neighbor] + euclideanHeuristic(points[neighbor], points[target]);
109+
fScore[neighbor] = priority;
110+
111+
openSet.enqueue(neighbor, priority);
112+
}
113+
}
114+
}
115+
116+
return null; // Return null if there's no path to the target
117+
}
118+
119+
// Define the graph as an adjacency list
120+
const graph = [
121+
[[1, 4], [7, 8]], // Node 0 connects to node 1 (weight 4), node 7 (weight 8)
122+
[[0, 4], [2, 8], [7, 11]], // Node 1 connects to node 0, node 2, node 7
123+
[[1, 8], [3, 7], [5, 4], [8, 2]], // Node 2 connects to several nodes
124+
[[2, 7], [4, 9], [5, 14]], // Node 3 connects to nodes 2, 4, 5
125+
[[3, 9], [5, 10]], // Node 4 connects to nodes 3 and 5
126+
[[2, 4], [3, 14], [4, 10], [6, 2]], // Node 5 connects to several nodes
127+
[[5, 2], [7, 1], [8, 6]], // Node 6 connects to nodes 5, 7, 8
128+
[[0, 8], [1, 11], [6, 1], [8, 7]], // Node 7 connects to several nodes
129+
[[2, 2], [6, 6], [7, 7]] // Node 8 connects to nodes 2, 6, 7
130+
];
131+
132+
// Define 2D coordinates for each node (these can be changed based on actual node positions)
133+
const points = [
134+
[0, 0], // Point for node 0
135+
[1, 2], // Point for node 1
136+
[2, 1], // Point for node 2
137+
[3, 5], // Point for node 3
138+
[4, 3], // Point for node 4
139+
[5, 6], // Point for node 5
140+
[6, 8], // Point for node 6
141+
[7, 10], // Point for node 7
142+
[8, 12] // Point for node 8
143+
];
144+
145+
// Call the aStar function with the graph, source node (0), and target node (4)
146+
const path = aStar(graph, 0, 4, points);
147+
148+
console.log('Shortest path from node 0 to node 4:', path);
149+
150+
/**
151+
* The function returns the optimal path from the source to the target node.
152+
* The heuristic used is Euclidean distance between nodes' 2D coordinates.
153+
*/
154+

0 commit comments

Comments
 (0)