我使用约翰逊的算法来找到通过一些任意顶点序列的最短路径。您可以在此处找到更多相关信息:problem description
以下是我如何解决这个问题,我首先使用Johnson算法来获得有向图的所有对最短路径,然后创建一个仅包含指定顶点的新图。最后,我在新创建的图形上实现Dijkstra算法,以找到从开始到结束的最短路径。我的代码如下所示:
package test;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import algorithm.graph.Dijkstra;
import algorithm.graph.Johnson;
import dataStructure.Graph.Edge;
import dataStructure.Graph.Vertex;
import dataStructure.Graph;
import java.io.IOException;
public class GraphsTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
// Compute all-pairs shortest paths using Johnson's algorithm
String topofilepath = "D:/Documents/Java/workspace/CodeCraftTest/data/case4/topo.csv";
final Graph<Integer> graph1 = new Graph<Integer>(Graph.TYPE.DIRECTED);
final int numOfVertices1 = BuildDirectedGraph(topofilepath, graph1);
System.out.println(numOfVertices1);
// System.out.println(graph1.getVertices());
final Map<Vertex<Integer>, Map<Vertex<Integer>, List<Edge<Integer>>>> path = Johnson.getAllPairsShortestPaths(graph1);
assertTrue("Directed graph contains a negative weight cycle.", (path != null));
// Code for debug
for (Vertex<Integer> vertex1 : path.keySet()){
final Map<Vertex<Integer>, List<Edge<Integer>>> map1 = path.get(vertex1);
for (Vertex<Integer> vertex2 : map1.keySet()){
final List<Edge<Integer>> list1 = map1.get(vertex2);
final Iterator<Edge<Integer>> iter1 = list1.iterator();
while (iter1.hasNext()){
Edge<Integer> e1 = (Edge<Integer>) iter1.next();
System.out.println(e1);
}
}
}
// Create a new graph that only contains the specified vertices in demand file
final Graph<Integer> graph2 = new Graph<Integer>(Graph.TYPE.DIRECTED);
String demandfilepath = "D:/Documents/Java/workspace/CodeCraftTest/data/case4/demand.csv";
final int numOfVertices2 = BuildDemandedGraph(demandfilepath, path, graph2);
System.out.println(numOfVertices2);
// System.out.println(graph2.getVertices());
// Implement Dijkstra's algorithm on the specified directed graph
BufferedReader b = null;
String line = "";
String csvSplitBy = ",";
int startID;
int endID;
final Graph.Vertex<Integer> start;
final Graph.Vertex<Integer> end;
try{
b = new BufferedReader(new FileReader(demandfilepath));
if ((line = b.readLine()) != null){
String[] entry = line.split(csvSplitBy);
startID = Integer.parseInt(entry[0]);
endID = Integer.parseInt(entry[1]);
start = new Graph.Vertex<Integer>(startID);
end = new Graph.Vertex<Integer>(endID);
final Graph.CostPathPair<Integer> pair1 = Dijkstra.getShortestPath(graph2, start, end);
// System.out.println(pair1.getPath());
}
}
catch(FileNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
finally{
if (b != null){
try{
b.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}
}
private static int BuildDirectedGraph(String topofilepath, Graph<Integer> graph) {
String csvFile = topofilepath;
BufferedReader br = null;
String line = "";
String csvSplitBy = ",";
String LinkID;
String SourceID;
String DestinationID;
String Cost;
int linkID;
int sourceID;
int destinationID;
int cost;
try{
br = new BufferedReader(new FileReader(csvFile));
while ((line = br.readLine()) != null)
{
String[] entry = line.split(csvSplitBy);
LinkID = entry[0];
SourceID = entry[1];
DestinationID = entry[2];
Cost = entry[3];
linkID = Integer.parseInt(LinkID);
sourceID = Integer.parseInt(SourceID);
destinationID = Integer.parseInt(DestinationID);
cost = Integer.parseInt(Cost);
final Graph.Vertex<Integer> v1 = new Graph.Vertex<Integer>(sourceID);
final Graph.Vertex<Integer> v2 = new Graph.Vertex<Integer>(destinationID);
final Graph.Edge<Integer> edge = new Graph.Edge<Integer>(cost, v1, v2);
if(graph.getVertices().contains(v1) == false)
{
graph.addVertex(v1);
}
if(graph.getVertices().contains(v2) == false)
{
graph.addVertex(v2);
}
graph.addEdge(edge);
}
}
catch(FileNotFoundException e)
{
e.printStackTrace();
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
if (br != null)
{
try{
br.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}
return graph.getVertices().size();
}
private static int BuildDemandedGraph(String demandfilepath, Map<Vertex<Integer>, Map<Vertex<Integer>, List<Edge<Integer>>>> path, Graph<Integer> graph)
{
String csvFile = demandfilepath;
BufferedReader br = null;
String line = "";
String csvSplitBy = ",";
int startID;
int endID;
int cost = 0;
try{
br = new BufferedReader(new FileReader(csvFile));
if ((line = br.readLine()) != null)
{
String[] entry = line.split(csvSplitBy);
startID = Integer.parseInt(entry[0]);
endID = Integer.parseInt(entry[1]);
final Graph.Vertex<Integer> start = new Graph.Vertex<Integer>(startID);
final Graph.Vertex<Integer> end = new Graph.Vertex<Integer>(endID);
if(graph.getVertices().contains(start) == false)
{
graph.addVertex(start);
}
if(graph.getVertices().contains(end) == false)
{
graph.addVertex(end);
}
String[] seqOfVertices = entry[2].split("\\|");
// System.out.println(seqOfVertices.length);
for (int i = 0; i < seqOfVertices.length; i++)
{
int vertexID = Integer.parseInt(seqOfVertices[i]);
final Graph.Vertex<Integer> v = new Graph.Vertex<Integer>(vertexID);
if (graph.getVertices().contains(v) == false)
{
graph.addVertex(v);
}
}
for (Graph.Vertex<Integer> v1 : graph.getVertices()){
for (Graph.Vertex<Integer> v2 : graph.getVertices()){
// debug
// System.out.println(v1.getValue());
for (Vertex<Integer> vertex : path.keySet()){
if (v1.equals(vertex)){
final Map<Vertex<Integer>, List<Edge<Integer>>> map1 = path.get(v1);
for (Vertex<Integer> v : map1.keySet()){
if (v.equals(v2)){
final List<Edge<Integer>> list1 = map1.get(v);
final Iterator<Edge<Integer>> iter1 = list1.iterator();
while(iter1.hasNext()){
Edge<Integer> e1 = (Edge<Integer>) iter1.next();
System.out.println(e1.getCost());
cost += e1.getCost();
}
}
}
final Graph.Edge<Integer> edge = new Graph.Edge<Integer>(cost, v1, v2);
graph.addEdge(edge);
}
if (v2.equals(vertex)){
final Map<Vertex<Integer>, List<Edge<Integer>>> map2 = path.get(v2);
for (Vertex<Integer> v : map2.keySet()){
if (v.equals(v1)){
final List<Edge<Integer>> list2 = map2.get(v);
final Iterator<Edge<Integer>> iter2 = list2.iterator();
while(iter2.hasNext()){
Edge<Integer> e2 = (Edge<Integer>) iter2.next();
cost += e2.getCost();
}
}
}
final Graph.Edge<Integer> edge = new Graph.Edge<Integer>(cost, v2, v1);
graph.addEdge(edge);
}
}
}
}
}
}
catch(FileNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
finally{
if (br != null){
try{
br.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}
return graph.getVertices().size();
}
}
Johnson的算法是由Justin Wetherell编写的,但在这种情况下它并不起作用,我已经验证图形是否已正确创建,但返回的所有最短路径都是空列表。我找不到问题。你能帮我发现一些可能导致这个问题的错误。
package algorithm.graph;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import dataStructure.Graph;
/**
* Johnson's algorithm is a way to find the shortest paths between all pairs of
* vertices in a sparse directed graph. It allows some of the edge weights to be
* negative numbers, but no negative-weight cycles may exist.
*
* Worst case: O(V^2 log V + VE)
*
* @author Justin Wetherell <phishman3579@gmail.com>
*/
public class Johnson {
private Johnson() { }
public static Map<Graph.Vertex<Integer>, Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>> getAllPairsShortestPaths(Graph<Integer> g) {
if (g == null)
throw (new NullPointerException("Graph must be non-NULL."));
// First, a new node 'connector' is added to the graph, connected by zero-weight edges to each of the other nodes.
final Graph<Integer> graph = new Graph<Integer>(g);
final Graph.Vertex<Integer> connector = new Graph.Vertex<Integer>(Integer.MAX_VALUE);
// Add the connector Vertex to all edges.
for (Graph.Vertex<Integer> v : graph.getVertices()) {
final int indexOfV = graph.getVertices().indexOf(v);
final Graph.Edge<Integer> edge = new Graph.Edge<Integer>(0, connector, graph.getVertices().get(indexOfV));
connector.addEdge(edge);
graph.getEdges().add(edge);
}
graph.getVertices().add(connector);
// Second, the Bellman鈥揊ord algorithm is used, starting from the new vertex 'connector', to find for each vertex 'v'
// the minimum weight h(v) of a path from 'connector' to 'v'. If this step detects a negative cycle, the algorithm is terminated.
final Map<Graph.Vertex<Integer>, Graph.CostPathPair<Integer>> costs = BellmanFord.getShortestPaths(graph, connector);
// Next the edges of the original graph are re-weighted using the values computed by the Bellman鈥揊ord algorithm: an edge
// from u to v, having length w(u,v), is given the new length w(u,v) + h(u) 鈭� h(v).
for (Graph.Edge<Integer> e : graph.getEdges()) {
final int weight = e.getCost();
final Graph.Vertex<Integer> u = e.getFromVertex();
final Graph.Vertex<Integer> v = e.getToVertex();
// Don't worry about the connector
if (u.equals(connector) || v.equals(connector))
continue;
// Adjust the costs
final int uCost = costs.get(u).getCost();
final int vCost = costs.get(v).getCost();
final int newWeight = weight + uCost - vCost;
e.setCost(newWeight);
}
// Finally, 'connector' is removed, and Dijkstra's algorithm is used to find the shortest paths from each node (s) to every
// other vertex in the re-weighted graph.
final int indexOfConnector = graph.getVertices().indexOf(connector);
graph.getVertices().remove(indexOfConnector);
for (Graph.Edge<Integer> e : connector.getEdges()) {
final int indexOfConnectorEdge = graph.getEdges().indexOf(e);
graph.getEdges().remove(indexOfConnectorEdge);
}
final Map<Graph.Vertex<Integer>, Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>> allShortestPaths = new HashMap<Graph.Vertex<Integer>, Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>>();
for (Graph.Vertex<Integer> v : graph.getVertices()) {
final Map<Graph.Vertex<Integer>, Graph.CostPathPair<Integer>> costPaths = Dijkstra.getShortestPaths(graph, v);
final Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>> paths = new HashMap<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>();
for (Graph.Vertex<Integer> v2 : costPaths.keySet()) {
final Graph.CostPathPair<Integer> pair = costPaths.get(v2);
paths.put(v2, pair.getPath());
}
allShortestPaths.put(v, paths);
}
return allShortestPaths;
}
}