19. Weighted directed graph
19.1 representation of edges
 API
 code
package chapter19; /** * @author Soil flavor * Date 2021/9/17 * @version 1.0 * Directed edge */ public class DirectedEdge { /** * starting point */ private final int v; /** * End */ private final int w; /** * weight */ private final double weight; /** * constructor * @param v * @param w * @param weight */ public DirectedEdge(int v, int w, double weight) { this.v = v; this.w = w; this.weight = weight; } /** * Get the starting point * @return */ public int from(){ return v; } /** * Get the end * @return */ public int to(){ return w; } /** * Get weight * @return */ public double getWeight(){ return weight; } }
19.2 implementation of weighted directed graph
 API
 code
package chapter19; import chapter03.Queue; import chapter17.Edge; /** * @author Soil flavor * Date 2021/9/16 * @version 1.0 * Weighted directed graph */ public class EdgeWeightedDigraph { /** * Number of vertices */ private final int vNum; /** * Number of edges */ private int eNum; /** * Adjacency table */ private Queue<DirectedEdge>[] adj; /** * constructor * * @param vNum */ public EdgeWeightedDigraph(int vNum) { // Number of initialization vertices this.vNum = vNum; // Number of initialized edges this.eNum = 0; // Initialize adjacency table this.adj = new Queue[vNum]; // Initializes an empty queue in the adjacency table for (int i = 0; i < vNum; i++) { this.adj[i] = new Queue<DirectedEdge>(); } } /** * Get the number of vertices * * @return */ public int getVNum() { return vNum; } /** * Get the number of edges * * @return */ public int getENum() { return eNum; } /** * Add an edge vw * * @param e */ public void addEdge(DirectedEdge e) { // Because it is a directed graph, edges are added to the adjacency table of the starting point int v = e.from(); this.adj[v].enQueue(e); // Number of edges plus 1 eNum++; } /** * Gets all edges indicated by vertex v * * @param v * @return */ public Queue<DirectedEdge> adj(int v) { return this.adj[v]; } /** * Gets all edges in a weighted directed graph * * @return */ public Queue<DirectedEdge> edges() { // Create a queue object to store all the edges Queue<DirectedEdge> allEdges = new Queue<>(); // Traverse each vertex in the graph and find the adjacency table of each vertex. The adjacency table stores each edge pointed out by the vertex for (int v = 0; v < vNum; v++) { // Traverse the adjacency table of vertex v and find each edge pointed out by V for (DirectedEdge e : adj(v)) { allEdges.enQueue(e); } } return allEdges; } }
20. Shortest path
20.1 definition and nature

definition
In a weighted digraph, the path with the smallest total weight among all paths from vertex s to vertex t

nature

The path is directional

Weight is not necessarily equivalent to distance
The weight can be distance, time, cost, etc. the smallest weight refers to the lowest cost

Only connected graphs are considered
Not all vertices in a graph are reachable. If s and t are not reachable, there is no shortest path between them. In order to simplify the problem, only connected graphs are considered here

The shortest path is not necessarily unique
There may be many paths with the least weight from one vertex to another. Here, just find one


Shortest path tree
 Given a weighted directed graph and a vertex s, a shortest path tree starting from s is a subgraph of the graph, which contains vertex s and all vertices reachable from S. The root node of the directed tree is s, and each path of the tree is the shortest path in the directed graph
20.2. Shortest path tree API design
20.3 relaxation technology
The word relaxation comes from life: a rubber band is tightly spread along a path between two vertices. If there is more than one path between the two vertices and there is a shorter path, the rubber band can be relaxed by transferring the rubber band to a shorter path
The simple principle of relaxation can be used to calculate the shortest path tree
In the API, two member variables edgeTo and distTo are needed to store edges and weights respectively. At first, given a graph G and vertex s, you only know the edges of the graph and the weights of these edges, and you know nothing else. At this time, the total weight disto[s]=0 of the shortest path from vertex s to vertex s; The total weight from vertex s to other vertices is infinite by default. With the implementation of the algorithm, we continue to use relaxation technology to process the edges and vertices of the graph, and update the data in edgeTo and distTo according to certain conditions. Finally, we can get the shortest path tree

Edge relaxation
Relaxing edge v  > W means checking whether the shortest path from s to w starts from s to v and then from v to w?
If yes, vw this edge needs to be added to the shortest path tree to update the contents in edgeTo and distTo:
edgeTo[w] = DirectedEdge object representing V  > w this edge
Distto [w] = distto [v] + V  > w this edge weight
If not, the edge V  > W is ignored

Vertex relaxation
The relaxation of vertices is based on the relaxation of edges. It is only necessary to relax all the edges pointed out by a vertex, and then the vertex is relaxed
For example:
To relax vertex v, just traverse the adjacency table of V and relax each edge, then vertex v is relaxed.
If the starting point is set to vertex 0, the process of finding the shortest path 0  > 2  > 7 > 3  > 6 from starting point 0 to vertex 6 is as follows:
20.4 implementation of Dijkstra algorithm
 code
package chapter20; import chapter03.Queue; import chapter07.IndexMinPriorityQueue; import chapter19.DirectedEdge; import chapter19.EdgeWeightedDigraph; /** * @author Soil flavor * Date 2021/9/18 * @version 1.0 * Dijestra shortest path algorithm */ public class DijkstraSP { /** * Last edge * The index represents the vertex * Value represents the last edge on the shortest path from vertex s to the current vertex */ private DirectedEdge[] edgeTo; /** * Total weight * The index represents the total weight of the shortest path from vertex s to the current vertex */ private double[] distTo; /** * Stores valid crosscutting edges between vertices in the tree and non vertices in the tree */ private IndexMinPriorityQueue<Double> pq; /** * constructor * According to a pair of weighted digraph G and vertex s, a shortest path tree object with vertex s is created * @param g * @param s */ public DijkstraSP(EdgeWeightedDigraph g,int s) { // Initialize edgeTo this.edgeTo = new DirectedEdge[g.getVNum()]; // Initialize distTo this.distTo = new double[g.getVNum()]; for(int i = 0;i<g.getVNum();i++){ // Default: positive infinity this.distTo[i] = Double.POSITIVE_INFINITY; } // Initialize pq this.pq = new IndexMinPriorityQueue<>(g.getVNum()); // Find the shortest path tree starting from vertex s in graph g // By default, let vertices enter the shortest path tree this.distTo[s] = 0.0; this.pq.insert(s,0.0); // Traversal pq while(!this.pq.isEmpty()){ relax(g,this.pq.delMin()); } } /** * Vertex v in relaxation weighted digraph g * @param g * @param v */ private void relax(EdgeWeightedDigraph g,int v){ // Traversing adjacency table of vertex v for (DirectedEdge edge : g.adj(v)) { // Gets the end point of the edge w int w = edge.to(); // Through relaxation technology: judge whether the shortest path from the starting point s to w should go through v: S  > v  > W if(distTo[v] + edge.getWeight() < distTo[w]){ // Data needs to be updated through v: // Total weight of the shortest path from s to w distTo[w] = distTo[v] + edge.getWeight(); // The last edge of the shortest path from s to w edgeTo[w] = edge; // Update pq if(pq.contains(w)){ pq.changeItem(w, edge.getWeight()); }else{ pq.insert(w, edge.getWeight()); } } } } /** * Gets the total weight of the shortest path from the starting point s to the vertex v * @param v * @return */ public double distTo(int v){ return distTo[v]; } /** * Judge whether it is reachable from the starting point s to the vertex v * @param v * @return */ public boolean hasPathTo(int v){ // The default is positive infinity return distTo[v] < Double.POSITIVE_INFINITY; } /** * All edges of the shortest path from the starting point s to the vertex v * @param v * @return */ public Queue<DirectedEdge> pathTo(int v){ // If it is not reachable from the starting point s to v, null is returned if(!hasPathTo(v)){ return null; } // Create a queue to receive the shortest path Queue<DirectedEdge> allEdges = new Queue<>(); // Starting from v, use the last edge of edgeTo to push back to the starting point s and store it in the queue while (true){ // Last edge of minimum path DirectedEdge e = edgeTo[v]; // The last edge of the starting point is null, exit the loop if(e == null){ break; } // Join queue allEdges.enQueue(e); // Update v and continue the cycle: because it is a directed edge, push back upward, and v is equal to the starting point of the edge v = e.from(); } return allEdges; } }
 test
package chapter20; import chapter03.Queue; import chapter19.DirectedEdge; import chapter19.EdgeWeightedDigraph; import org.junit.Test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * @author Soil flavor * Date 2021/9/18 * @version 1.0 * Test weighted directed graph shortest path algorithm */ public class DijkstraSPTest { @Test public void test() throws IOException { // Prepare a weighted directed graph BufferedReader br = new BufferedReader(new InputStreamReader(DijkstraSPTest.class.getClassLoader().getResourceAsStream("min_route_test.txt"))); // Number of vertices int total = Integer.parseInt(br.readLine()); // Weighted directed graph EdgeWeightedDigraph g = new EdgeWeightedDigraph(total); // Number of edges int edgeNum = Integer.parseInt(br.readLine()); // Read edges in turn for (int i = 0; i < edgeNum; i++) { String[] s = br.readLine().split(" "); int v = Integer.parseInt(s[0]); int w = Integer.parseInt(s[1]); double weight = Double.parseDouble(s[2]); // Create weighted directed edges DirectedEdge edge = new DirectedEdge(v, w, weight); // Adding edges to a graph g.addEdge(edge); } // Create DijkstraSP object and find the shortest path tree DijkstraSP sp = new DijkstraSP(g, 0); // Find shortest path Queue<DirectedEdge> edges = sp.pathTo(6); // Print for (DirectedEdge e : edges) { int v = e.from(); int w = e.to(); double weight = e.getWeight(); System.out.println(v + "  " + w + " : " + weight); } } }
 Test data: min_route_test.txt
8 15 4 5 0.35 5 4 0.35 4 7 0.37 5 7 0.28 7 5 0.28 5 1 0.32 0 4 0.38 0 2 0.26 7 3 0.39 1 3 0.29 2 7 0.34 6 2 0.40 3 6 0.52 6 0 0.58 6 4 0.93
 Operation results
3  6 : 0.52 7  3 : 0.39 2  7 : 0.34 0  2 : 0.26