pub fn johnson<G, F, K>(
graph: G,
edge_cost: F,
) -> Result<HashMap<(G::NodeId, G::NodeId), K>, NegativeCycle>where
G: IntoEdges + IntoNodeIdentifiers + NodeIndexable + Visitable,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> K,
K: BoundedMeasure + Copy + Sub<K, Output = K>,
Expand description
[Generic] Johnson algorithm for all pairs shortest path problem.
Сompute the lengths of shortest paths in a weighted graph with positive or negative edge weights, but no negative cycles.
The time complexity of this implementation is O(|V||E|log(|V|) + |V|²*log(|V|)),
which is faster than floyd_warshall
on sparse graphs and slower on dense ones.
If you are working with a sparse graph that is guaranteed to have no negative weights,
it’s preferable to run dijkstra
several times.
There is also a parallel implementation parallel_johnson
, available under the rayon
feature.
§Arguments
graph
: weighted graph.edge_cost
: closure that returns cost of a particular edge.
§Returns
Err
: if graph contains negative cycle.Ok
:HashMap
of shortest distances.
§Complexity
- Time complexity: O(|V||E|log(|V|) + |V|²log(|V|)) since the implementation is based on
dijkstra
. - Auxiliary space: O(|V|² + |V||E|).
where |V| is the number of nodes and |E| is the number of edges.
§Examples
use petgraph::{prelude::*, Graph, Directed};
use petgraph::algo::johnson;
use std::collections::HashMap;
let mut graph: Graph<(), i32, Directed> = Graph::new();
let a = graph.add_node(());
let b = graph.add_node(());
let c = graph.add_node(());
let d = graph.add_node(());
graph.extend_with_edges(&[
(a, b, 1),
(a, c, 4),
(a, d, 10),
(b, c, 2),
(b, d, 2),
(c, d, 2)
]);
// ----- b --------
// | ^ | 2
// | 1 | 4 v
// 2 | a ------> c
// | 10 | | 2
// | v v
// ---> d <-------
let expected_res: HashMap<(NodeIndex, NodeIndex), i32> = [
((a, a), 0), ((a, b), 1), ((a, c), 3), ((a, d), 3),
((b, b), 0), ((b, c), 2), ((b, d), 2),
((c, c), 0), ((c, d), 2),
((d, d), 0),
].iter().cloned().collect();
let res = johnson(&graph, |edge| {
*edge.weight()
}).unwrap();
let nodes = [a, b, c, d];
for node1 in &nodes {
for node2 in &nodes {
assert_eq!(res.get(&(*node1, *node2)), expected_res.get(&(*node1, *node2)));
}
}