Using Single Source Shortest Path Algorithm to find the longest path

Click For Summary
SUMMARY

The discussion centers on implementing the Single Source Shortest Path Algorithm using a Weighted Directed Graph in JavaScript. The user successfully created a graph structure and computed the shortest path from node A to all other nodes, yielding correct results. However, the attempt to find the longest path by negating edge weights produced incorrect outputs for certain nodes. The issue was traced back to the getEdgesFromNode method, which required a condition to filter edges correctly based on direction.

PREREQUISITES
  • JavaScript programming proficiency
  • Understanding of graph theory concepts, specifically directed acyclic graphs (DAGs)
  • Familiarity with depth-first search (DFS) algorithms
  • Knowledge of topological sorting techniques
NEXT STEPS
  • Investigate the implementation of depth-first search (DFS) in JavaScript
  • Learn about topological sorting algorithms and their applications in graph theory
  • Explore debugging techniques for JavaScript to identify logical errors in algorithms
  • Study the mathematical principles behind graph algorithms, particularly the transformation of edge weights
USEFUL FOR

Software developers, particularly those working with algorithms, data structures, and graph theory, will benefit from this discussion. It is also valuable for educators and students in computer science looking to deepen their understanding of pathfinding algorithms.

Monsterboy
Messages
305
Reaction score
96
Homework Statement
Using Single Source Shortest Path Algorithm to find the longest path
Relevant Equations
First multiply the edge weights by -1 and find the shortest path, then multiply the result by -1 again.
This is the weighted, directed acyclic graph I created in JavaScript

Code:
class WeightedDirectedGraph {
constructor() {
this.adjacencyList = {};
}

addNode(node) {
if(!this.adjacencyList[node]) {
this.adjacencyList[node] = [];
}
}

addEdge(node1, node2, direction, weight) {
this.adjacencyList[node1].push({node: node2, direction: direction, weight: weight });
this.adjacencyList[node2].push({node: node1, direction: direction, weight: weight });
}

getEdgesFromNode(node) {
let edges = []
if(this.adjacencyList[node]) {
for (let i = 0; i < this.adjacencyList[node].length; i++) {
edges.push(this.adjacencyList[node][i]);
}
}
return edges;
}
}

let graph = new WeightedDirectedGraph();
graph.addNode("A")
graph.addNode("B")
graph.addNode("C")
graph.addNode("D")
graph.addNode("E")
graph.addNode("F")
graph.addNode("G")
graph.addNode("H")

graph.addEdge("A", "B", {from: "A", to: "B"}, 3)
graph.addEdge("A", "C", {from: "A", to: "C"}, 6)
graph.addEdge("B", "C", {from: "B", to: "C"}, 4)
graph.addEdge("B", "D", {from: "B", to: "D"}, 4)
graph.addEdge("B", "E", {from: "B", to: "E"}, 11)
graph.addEdge("C", "D", {from: "C", to: "D"}, 8)
graph.addEdge("C", "G", {from: "C", to: "G"}, 11)
graph.addEdge("D", "E", {from: "D", to: "E"}, -4)
graph.addEdge("D", "F", {from: "D", to: "F"}, 5)
graph.addEdge("D", "G", {from: "D", to: "G"}, 2)
graph.addEdge("E", "H", {from: "E", to: "H"}, 9)
graph.addEdge("F", "H", {from: "F", to: "H"}, 1)
graph.addEdge("G", "H", {from: "G", to: "H"}, 2)

I will attach an image to visualize this graph
1650271182294.png


This is the topological sort code

Code:
function topSort(graph) {
let N = Object.keys(graph.adjacencyList).length
let V = {};  // visited nodes list
let ordering = [];
for (let node in graph.adjacencyList) {
V[node] = false;
ordering.push(0);
}
let i = N - 1;
for (let node in graph.adjacencyList) {
if(V[node] === false) {
i = dfs(i, node, V, ordering, graph)
}
}
return ordering;
}

function dfs(i, node, V, ordering, graph) {
V[node] = true;
let edges = graph.getEdgesFromNode(node)
for (let k = 0; k < edges.length; k++) {
if (V[edges[k].direction.to] === false) {
i = dfs(i, edges[k].direction.to, V, ordering, graph)
}
}
ordering[i] = node;
return i - 1;
}

function dagShortestPath(graph, startNode) {
let topSortResult = topSort(graph);
let distances = {};
topSortResult.forEach((node) => {
distances[node] = Infinity;
})

distances[startNode] = 0;
for( let i = 0; i < topSortResult.length; i++) {
let nodeIndex = topSortResult[i];
let edges = graph.getEdgesFromNode(nodeIndex);
if (edges.length !== 0) {
let newDist;
for (let i = 0; i < edges.length; i++) {
newDist = distances[nodeIndex] + edges[i].weight;
if (distances[edges[i].direction.to] === Infinity) {
distances[edges[i].direction.to] = newDist;
}
else if (newDist > -1) {
distances[edges[i].direction.to] = Math.min(distances[edges[i].direction.to], newDist);
}
}
}
}
return distances;
}
I tested the code for the shortest path from node A to all other nodes, I am getting it right.
The output is { A: 0, B: 3, C: 6, D: 7, G: 9, F: 12, E: 3, H: 11 }

This is the code for the longest path.

Code:
function LongestPath(graph, startNode) {
  let topSortResult = topSort(graph);
  let distances = {};
  topSortResult.forEach((node) => {
    distances[node] = Infinity;
  })
  distances[startNode] = 0;
  // multiply all edge weights by -1
  for (let node in graph.adjacencyList) {
    for (let i = 0; i < graph.adjacencyList[node].length; i++) {
      graph.adjacencyList[node][i].weight = graph.adjacencyList[node][i].weight*-1;
    }
  }
  console.log(graph.adjacencyList);
  let longestDistances = dagShortestPath(graph, startNode);
// multiply all edge weights by -1 after getting longest path
for (let node in graph.adjacencyList) {
  for (let i = 0; i < graph.adjacencyList[node].length; i++) {
    graph.adjacencyList[node][i].weight = graph.adjacencyList[node][i].weight*-1;
  }
}
  for (let node in longestDistances) {
    longestDistances[node] *= -1;
  }
  return longestDistances;
}

I am getting this wrong, even though the only change is the multiplication of the edge weights with -1.
Output is { A: -0, B: 3, C: 6, D: 7, G: 17, F: 12, E: 14, H: 19 } although for some nodes the output is right.

I took the image and the idea of multiplying by -1 from this free YouTube course on graph algorithms from freeCodeCamp.org channel -

Algorithms Course - Graph Theory Tutorial from a Google Engineer​

 
Last edited:
Physics news on Phys.org
If you set the language to javascript as in the code below it is much easier to read.

JavaScript:
class WeightedDirectedGraph {
constructor() {
this.adjacencyList = {};
}

addNode(node) {
if(!this.adjacencyList[node]) {
this.adjacencyList[node] = [];
}
}

addEdge(node1, node2, direction, weight) {
this.adjacencyList[node1].push({node: node2, direction: direction, weight: weight });
this.adjacencyList[node2].push({node: node1, direction: direction, weight: weight });
}

getEdgesFromNode(node) {
let edges = []
if(this.adjacencyList[node]) {
for (let i = 0; i < this.adjacencyList[node].length; i++) {
edges.push(this.adjacencyList[node][i]);
}
}
return edges;
}
}

let graph = new WeightedDirectedGraph();
graph.addNode("A")
graph.addNode("B")
graph.addNode("C")
graph.addNode("D")
graph.addNode("E")
graph.addNode("F")
graph.addNode("G")
graph.addNode("H")

graph.addEdge("A", "B", {from: "A", to: "B"}, 3)
graph.addEdge("A", "C", {from: "A", to: "C"}, 6)
graph.addEdge("B", "C", {from: "B", to: "C"}, 4)
graph.addEdge("B", "D", {from: "B", to: "D"}, 4)
graph.addEdge("B", "E", {from: "B", to: "E"}, 11)
graph.addEdge("C", "D", {from: "C", to: "D"}, 8)
graph.addEdge("C", "G", {from: "C", to: "G"}, 11)
graph.addEdge("D", "E", {from: "D", to: "E"}, -4)
graph.addEdge("D", "F", {from: "D", to: "F"}, 5)
graph.addEdge("D", "G", {from: "D", to: "G"}, 2)
graph.addEdge("E", "H", {from: "E", to: "H"}, 9)
graph.addEdge("F", "H", {from: "F", to: "H"}, 1)
graph.addEdge("G", "H", {from: "G", to: "H"}, 2)

And if you add indentation it is easier still:
JavaScript:
class WeightedDirectedGraph {
  constructor() {
    this.adjacencyList = {};
  }

  addNode(node) {
    if(!this.adjacencyList[node]) {
      this.adjacencyList[node] = [];
    }
  }

  addEdge(node1, node2, direction, weight) {
    this.adjacencyList[node1].push({ node: node2, direction: direction, weight: weight });
    this.adjacencyList[node2].push({ node: node1, direction: direction, weight: weight });
  }

  getEdgesFromNode(node) {
    let edges = []
    if(this.adjacencyList[node]) {
      for (let i = 0; i < this.adjacencyList[node].length; i++) {
        edges.push(this.adjacencyList[node][i]);
      }
    }
    return edges;
  }
}

This bug might be hard to track down. I suggest you focus on what each line of code is doing when it decides that the longest path from A to C is 6 instead of 7 as this is the simplest "error".
 
  • Like
Likes   Reactions: Monsterboy
Is the idea of multiplying the edge weights by -1 to find the longest path right ? Does it actually work ?
 
In the graph class definition, this method was returning wrong edges.
JavaScript:
 getEdgesFromNode(node) {
    let edges = []
    if(this.adjacencyList[node]) {
      for (let i = 0; i < this.adjacencyList[node].length; i++) {
        if(this.adjacencyList[node][i].direction.from === node) {   // this condition fixed the issue.
          let edge = {direction: this.adjacencyList[node][i].direction, weight: this.adjacencyList[node][i].weight}
          edges.push(edge);  
        }
      }
    }
    return edges; 
  }
 
  • Like
Likes   Reactions: pbuk
Monsterboy said:
In the graph class definition, this method was returning wrong edges.
Glad you tracked it down, well done.
 

Similar threads

  • · Replies 5 ·
Replies
5
Views
3K
  • · Replies 5 ·
Replies
5
Views
3K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 7 ·
Replies
7
Views
3K
  • · Replies 8 ·
Replies
8
Views
2K
  • · Replies 3 ·
Replies
3
Views
2K
  • · Replies 11 ·
Replies
11
Views
2K
  • · Replies 13 ·
Replies
13
Views
2K
  • · Replies 5 ·
Replies
5
Views
3K