package com.jwetherell.algorithms.graph; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.jwetherell.algorithms.data_structures.Graph; /** * Bellman-Ford's shortest path. Works on both negative and positive weighted * edges. Also detects negative weight cycles. Returns a tuple of total cost of * shortest path and the path. * * Worst case: O(|V| |E|) * * https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm * * @author Justin Wetherell */ public class BellmanFord { private BellmanFord() { } /** * Get shortest path for all vertices */ public static Map, Graph.CostPathPair> getShortestPaths(Graph graph, Graph.Vertex start) { final Map, List>> paths = new HashMap, List>>(); final Map, Graph.CostVertexPair> costs = new HashMap, Graph.CostVertexPair>(); getShortestPath(graph, start, paths, costs); final Map, Graph.CostPathPair> map = new HashMap, Graph.CostPathPair>(); for (Graph.CostVertexPair pair : costs.values()) { final int cost = pair.getCost(); final Graph.Vertex vertex = pair.getVertex(); final List> path = paths.get(vertex); map.put(vertex, new Graph.CostPathPair(cost, path)); } return map; } /** * Get shortest path to from 'start' to 'end' vertices */ public static Graph.CostPathPair getShortestPath(Graph graph, Graph.Vertex start, Graph.Vertex end) { if (graph == null) throw (new NullPointerException("Graph must be non-NULL.")); final Map, List>> paths = new HashMap, List>>(); final Map, Graph.CostVertexPair> costs = new HashMap, Graph.CostVertexPair>(); return getShortestPath(graph, start, end, paths, costs); } private static Graph.CostPathPair getShortestPath(Graph graph, Graph.Vertex start, Graph.Vertex end, Map, List>> paths, Map, Graph.CostVertexPair> costs) { if (end == null) throw (new NullPointerException("end must be non-NULL.")); getShortestPath(graph, start, paths, costs); final Graph.CostVertexPair pair = costs.get(end); final List> list = paths.get(end); return (new Graph.CostPathPair(pair.getCost(), list)); } private static void getShortestPath(Graph graph, Graph.Vertex start, Map, List>> paths, Map, Graph.CostVertexPair> costs) { if (graph == null) throw (new NullPointerException("Graph must be non-NULL.")); if (start == null) throw (new NullPointerException("start must be non-NULL.")); for (Graph.Vertex v : graph.getVertices()) paths.put(v, new ArrayList>()); // All vertices are INFINITY unless it's the start vertices for (Graph.Vertex v : graph.getVertices()) if (v.equals(start)) costs.put(v, new Graph.CostVertexPair(0, v)); else costs.put(v, new Graph.CostVertexPair(Integer.MAX_VALUE, v)); boolean negativeCycleCheck = false; for (int i = 0; i < graph.getVertices().size(); i++) { // If it's the last vertices, perform a negative weight cycle check. // The graph should be finished by the size()-1 time through this loop. if (i == (graph.getVertices().size() - 1)) negativeCycleCheck = true; // Compute costs to all vertices for (Graph.Edge e : graph.getEdges()) { final Graph.CostVertexPair pair = costs.get(e.getToVertex()); final Graph.CostVertexPair lowestCostToThisVertex = costs.get(e.getFromVertex()); // If the cost of the from vertex is MAX_VALUE then treat as INIFINITY. if (lowestCostToThisVertex.getCost() == Integer.MAX_VALUE) continue; final int cost = lowestCostToThisVertex.getCost() + e.getCost(); if (cost < pair.getCost()) { // Found a shorter path to a reachable vertex pair.setCost(cost); if (negativeCycleCheck) { // Uhh ohh... negative weight cycle throw new IllegalArgumentException("Graph contains a negative weight cycle."); } final List> list = paths.get(e.getToVertex()); list.clear(); list.addAll(paths.get(e.getFromVertex())); list.add(e); } } } } }