2016-12-28 23:54:51 +01:00
|
|
|
|
\chapter{Shortest paths}
|
|
|
|
|
|
2017-01-07 19:08:47 +01:00
|
|
|
|
\index{shortest path}
|
|
|
|
|
|
|
|
|
|
Finding the shortest path between two nodes
|
2017-02-05 10:51:38 +01:00
|
|
|
|
of a graph
|
|
|
|
|
is an important problem that has many
|
2017-01-07 19:08:47 +01:00
|
|
|
|
applications in practice.
|
|
|
|
|
For example, a natural problem in a road network
|
2017-02-17 21:13:30 +01:00
|
|
|
|
is to calculate the length of the shortest route
|
2017-01-07 19:08:47 +01:00
|
|
|
|
between two cities, given the lengths of the roads.
|
|
|
|
|
|
|
|
|
|
In an unweighted graph, the length of a path equals
|
|
|
|
|
the number of edges in the path and we can
|
2017-02-05 10:51:38 +01:00
|
|
|
|
simply use breadth-first search to find
|
2017-01-07 19:08:47 +01:00
|
|
|
|
the shortest path.
|
|
|
|
|
However, in this chapter we concentrate on
|
2017-02-17 21:13:30 +01:00
|
|
|
|
weighted graphs
|
|
|
|
|
where more sophisticated algorithms
|
2017-02-05 10:51:38 +01:00
|
|
|
|
are needed
|
2017-01-07 19:08:47 +01:00
|
|
|
|
for finding shortest paths.
|
|
|
|
|
|
|
|
|
|
\section{Bellman–Ford algorithm}
|
|
|
|
|
|
|
|
|
|
\index{Bellman–Ford algorithm}
|
|
|
|
|
|
2017-02-26 12:51:38 +01:00
|
|
|
|
The \key{Bellman–Ford algorithm}\footnote{The algorithm is named after
|
|
|
|
|
R. E. Bellman and L. R. Ford who published it independently
|
|
|
|
|
in 1958 and 1956, respectively \cite{bel58,for56a}.} finds the
|
2017-02-05 10:51:38 +01:00
|
|
|
|
shortest paths from a starting node to all
|
2017-01-07 19:08:47 +01:00
|
|
|
|
other nodes in the graph.
|
2017-02-05 10:51:38 +01:00
|
|
|
|
The algorithm can process all kinds of graphs,
|
|
|
|
|
provided that the graph does not contain a
|
2017-01-07 19:08:47 +01:00
|
|
|
|
cycle with negative length.
|
|
|
|
|
If the graph contains a negative cycle,
|
|
|
|
|
the algorithm can detect this.
|
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
The algorithm keeps track of distances
|
2017-01-07 19:08:47 +01:00
|
|
|
|
from the starting node to other nodes.
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Initially, the distance to the starting node is 0
|
|
|
|
|
and the distance to all other nodes in infinite.
|
|
|
|
|
The algorithm reduces the distances by finding
|
2017-01-07 19:08:47 +01:00
|
|
|
|
edges that shorten the paths until it is not
|
2017-02-05 10:51:38 +01:00
|
|
|
|
possible to reduce any distance.
|
2017-01-07 19:08:47 +01:00
|
|
|
|
|
|
|
|
|
\subsubsection{Example}
|
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Let us consider how the Bellman–Ford algorithm
|
2017-01-07 19:08:47 +01:00
|
|
|
|
works in the following graph:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {1};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {2};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {3};
|
|
|
|
|
\node[draw, circle] (4) at (4,1) {4};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {5};
|
|
|
|
|
\node[color=red] at (1,3+0.55) {$0$};
|
|
|
|
|
\node[color=red] at (4,3+0.55) {$\infty$};
|
|
|
|
|
\node[color=red] at (1,1-0.55) {$\infty$};
|
|
|
|
|
\node[color=red] at (4,1-0.55) {$\infty$};
|
|
|
|
|
\node[color=red] at (6,2-0.55) {$\infty$};
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:2] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:$-2$] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:5] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Each node in the graph is assigned a distance.
|
|
|
|
|
Initially, the distance to the starting node is 0,
|
|
|
|
|
and the distance to all other nodes is infinite.
|
2017-01-07 19:08:47 +01:00
|
|
|
|
|
2017-02-17 21:13:30 +01:00
|
|
|
|
The algorithm searches for edges that reduce distances.
|
|
|
|
|
First, all edges from node 1 reduce distances:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {1};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {2};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {3};
|
|
|
|
|
\node[draw, circle] (4) at (4,1) {4};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {5};
|
|
|
|
|
\node[color=red] at (1,3+0.55) {$0$};
|
|
|
|
|
\node[color=red] at (4,3+0.55) {$2$};
|
|
|
|
|
\node[color=red] at (1,1-0.55) {$3$};
|
|
|
|
|
\node[color=red] at (4,1-0.55) {$7$};
|
|
|
|
|
\node[color=red] at (6,2-0.55) {$\infty$};
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:2] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:$-2$] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:5] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (2);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (3);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (4);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
2017-01-07 19:08:47 +01:00
|
|
|
|
After this, edges
|
|
|
|
|
$2 \rightarrow 5$ and $3 \rightarrow 4$
|
2017-02-17 21:13:30 +01:00
|
|
|
|
reduce distances:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {1};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {2};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {3};
|
|
|
|
|
\node[draw, circle] (4) at (4,1) {4};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {5};
|
|
|
|
|
\node[color=red] at (1,3+0.55) {$0$};
|
|
|
|
|
\node[color=red] at (4,3+0.55) {$2$};
|
|
|
|
|
\node[color=red] at (1,1-0.55) {$3$};
|
|
|
|
|
\node[color=red] at (4,1-0.55) {$1$};
|
|
|
|
|
\node[color=red] at (6,2-0.55) {$7$};
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:2] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:$-2$] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:5] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (2) -- (5);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (3) -- (4);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Finally, there is one more change:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {1};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {2};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {3};
|
|
|
|
|
\node[draw, circle] (4) at (4,1) {4};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {5};
|
|
|
|
|
\node[color=red] at (1,3+0.55) {$0$};
|
|
|
|
|
\node[color=red] at (4,3+0.55) {$2$};
|
|
|
|
|
\node[color=red] at (1,1-0.55) {$3$};
|
|
|
|
|
\node[color=red] at (4,1-0.55) {$1$};
|
|
|
|
|
\node[color=red] at (6,2-0.55) {$3$};
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:2] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:$-2$] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:5] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (4) -- (5);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
After this, no edge can reduce any distance.
|
2017-01-07 19:08:47 +01:00
|
|
|
|
This means that the distances are final
|
|
|
|
|
and we have successfully
|
|
|
|
|
calculated the shortest distance
|
|
|
|
|
from the starting node to all other nodes.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-02-17 21:13:30 +01:00
|
|
|
|
For example, the shortest distance 3
|
2017-01-07 19:08:47 +01:00
|
|
|
|
from node 1 to node 5 corresponds to
|
|
|
|
|
the following path:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {1};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {2};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {3};
|
|
|
|
|
\node[draw, circle] (4) at (4,1) {4};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {5};
|
|
|
|
|
\node[color=red] at (1,3+0.55) {$0$};
|
|
|
|
|
\node[color=red] at (4,3+0.55) {$2$};
|
|
|
|
|
\node[color=red] at (1,1-0.55) {$3$};
|
|
|
|
|
\node[color=red] at (4,1-0.55) {$1$};
|
|
|
|
|
\node[color=red] at (6,2-0.55) {$3$};
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:2] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:$-2$] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:5] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (3);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (3) -- (4);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (4) -- (5);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
|
|
|
|
|
2017-01-07 19:08:47 +01:00
|
|
|
|
\subsubsection{Implementation}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:08:47 +01:00
|
|
|
|
The following implementation of the
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Bellman–Ford algorithm finds the shortest distances
|
2017-01-07 19:08:47 +01:00
|
|
|
|
from a node $x$ to all other nodes in the graph.
|
|
|
|
|
The code assumes that the graph is stored
|
2017-04-17 14:27:43 +02:00
|
|
|
|
as an edge list \texttt{edges}
|
|
|
|
|
that consists of tuples of the form $(a,b,w)$,
|
|
|
|
|
meaning that there is an edge from node $a$ to node $b$
|
|
|
|
|
with weight $w$.
|
2017-01-07 19:08:47 +01:00
|
|
|
|
|
|
|
|
|
The algorithm consists of $n-1$ rounds,
|
|
|
|
|
and on each round the algorithm goes through
|
2017-02-17 21:13:30 +01:00
|
|
|
|
all edges of the graph and tries to
|
2017-02-05 10:51:38 +01:00
|
|
|
|
reduce the distances.
|
2017-04-17 12:58:04 +02:00
|
|
|
|
The algorithm constructs an array \texttt{distance}
|
2017-01-07 19:08:47 +01:00
|
|
|
|
that will contain the distance from $x$
|
|
|
|
|
to all nodes in the graph.
|
2017-04-17 13:38:33 +02:00
|
|
|
|
The constant \texttt{INF} denotes an infinite distance.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
2017-04-17 14:27:43 +02:00
|
|
|
|
for (int i = 1; i <= n; i++) dist[i] = INF;
|
|
|
|
|
dist[x] = 0;
|
2016-12-28 23:54:51 +01:00
|
|
|
|
for (int i = 1; i <= n-1; i++) {
|
2017-04-17 14:27:43 +02:00
|
|
|
|
for (auto e : edges) {
|
|
|
|
|
int a, b, w;
|
|
|
|
|
tie(a, b, w) = e;
|
|
|
|
|
dist[b] = min(dist[b], dist[a]+w);
|
2016-12-28 23:54:51 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
The time complexity of the algorithm is $O(nm)$,
|
|
|
|
|
because the algorithm consists of $n-1$ rounds and
|
|
|
|
|
iterates through all $m$ edges during a round.
|
2017-01-07 19:08:47 +01:00
|
|
|
|
If there are no negative cycles in the graph,
|
2017-02-05 10:51:38 +01:00
|
|
|
|
all distances are final after $n-1$ rounds,
|
2017-01-07 19:08:47 +01:00
|
|
|
|
because each shortest path can contain at most $n-1$ edges.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:08:47 +01:00
|
|
|
|
In practice, the final distances can usually
|
2017-02-17 21:13:30 +01:00
|
|
|
|
be found faster than in $n-1$ rounds.
|
2017-01-07 19:08:47 +01:00
|
|
|
|
Thus, a possible way to make the algorithm more efficient
|
2017-02-05 10:51:38 +01:00
|
|
|
|
is to stop the algorithm if no distance
|
|
|
|
|
can be reduced during a round.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-04-17 12:58:04 +02:00
|
|
|
|
\subsubsection{Negative cycles}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:08:47 +01:00
|
|
|
|
\index{negative cycle}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
The Bellman–Ford algorithm can be also used to
|
2017-01-07 19:08:47 +01:00
|
|
|
|
check if the graph contains a cycle with negative length.
|
|
|
|
|
For example, the graph
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
|
\node[draw, circle] (1) at (0,0) {$1$};
|
|
|
|
|
\node[draw, circle] (2) at (2,1) {$2$};
|
|
|
|
|
\node[draw, circle] (3) at (2,-1) {$3$};
|
|
|
|
|
\node[draw, circle] (4) at (4,0) {$4$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:$3$] {} (2);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:$1$] {} (4);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=below:$5$] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:$-7$] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=right:$2$] {} (3);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
|
|
|
|
\noindent
|
2017-01-07 19:08:47 +01:00
|
|
|
|
contains a negative cycle
|
|
|
|
|
$2 \rightarrow 3 \rightarrow 4 \rightarrow 2$
|
|
|
|
|
with length $-4$.
|
|
|
|
|
|
|
|
|
|
If the graph contains a negative cycle,
|
|
|
|
|
we can shorten a path that contains the cycle
|
|
|
|
|
infinitely many times by repeating the cycle
|
|
|
|
|
again and again.
|
|
|
|
|
Thus, the concept of a shortest path
|
2017-02-05 10:51:38 +01:00
|
|
|
|
is not meaningful in this situation.
|
2017-01-07 19:08:47 +01:00
|
|
|
|
|
|
|
|
|
A negative cycle can be detected
|
|
|
|
|
using the Bellman–Ford algorithm by
|
|
|
|
|
running the algorithm for $n$ rounds.
|
2017-02-05 10:51:38 +01:00
|
|
|
|
If the last round reduces any distance,
|
2017-01-07 19:08:47 +01:00
|
|
|
|
the graph contains a negative cycle.
|
2017-02-17 21:13:30 +01:00
|
|
|
|
Note that this algorithm can be used to
|
|
|
|
|
search for
|
2017-01-07 19:08:47 +01:00
|
|
|
|
a negative cycle in the whole graph
|
|
|
|
|
regardless of the starting node.
|
|
|
|
|
|
|
|
|
|
\subsubsection{SPFA algorithm}
|
|
|
|
|
|
|
|
|
|
\index{SPFA algorithm}
|
|
|
|
|
|
2017-02-21 00:17:36 +01:00
|
|
|
|
The \key{SPFA algorithm} (''Shortest Path Faster Algorithm'') \cite{fan94}
|
2017-02-05 10:51:38 +01:00
|
|
|
|
is a variant of the Bellman–Ford algorithm,
|
2017-01-07 19:08:47 +01:00
|
|
|
|
that is often more efficient than the original algorithm.
|
2017-02-17 21:13:30 +01:00
|
|
|
|
The SPFA algorithm does not go through all the edges on each round,
|
2017-01-07 19:08:47 +01:00
|
|
|
|
but instead, it chooses the edges to be examined
|
|
|
|
|
in a more intelligent way.
|
|
|
|
|
|
|
|
|
|
The algorithm maintains a queue of nodes that might
|
2017-02-05 10:51:38 +01:00
|
|
|
|
be used for reducing the distances.
|
2017-01-07 19:08:47 +01:00
|
|
|
|
First, the algorithm adds the starting node $x$
|
|
|
|
|
to the queue.
|
|
|
|
|
Then, the algorithm always processes the
|
|
|
|
|
first node in the queue, and when an edge
|
2017-02-05 10:51:38 +01:00
|
|
|
|
$a \rightarrow b$ reduces a distance,
|
2017-02-17 21:13:30 +01:00
|
|
|
|
node $b$ is added to the queue.
|
2017-04-17 14:27:43 +02:00
|
|
|
|
%
|
|
|
|
|
% The following implementation uses a
|
|
|
|
|
% \texttt{queue} \texttt{q}.
|
|
|
|
|
% In addition, an array \texttt{inqueue} indicates
|
|
|
|
|
% if a node is already in the queue,
|
|
|
|
|
% in which case the algorithm does not add
|
|
|
|
|
% the node to the queue again.
|
|
|
|
|
%
|
|
|
|
|
% \begin{lstlisting}
|
|
|
|
|
% for (int i = 1; i <= n; i++) distance[i] = INF;
|
|
|
|
|
% distance[x] = 0;
|
|
|
|
|
% q.push(x);
|
|
|
|
|
% while (!q.empty()) {
|
|
|
|
|
% int a = q.front(); q.pop();
|
|
|
|
|
% inqueue[a] = false;
|
|
|
|
|
% for (auto b : v[a]) {
|
|
|
|
|
% if (distance[a]+b.second < distance[b.first]) {
|
|
|
|
|
% distance[b.first] = distance[a]+b.second;
|
|
|
|
|
% if (!inqueue[b]) {q.push(b); inqueue[b] = true;}
|
|
|
|
|
% }
|
|
|
|
|
% }
|
|
|
|
|
% }
|
|
|
|
|
% \end{lstlisting}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:08:47 +01:00
|
|
|
|
The efficiency of the SPFA algorithm depends
|
|
|
|
|
on the structure of the graph:
|
2017-02-17 21:13:30 +01:00
|
|
|
|
the algorithm is often efficient,
|
2017-01-07 19:08:47 +01:00
|
|
|
|
but its worst case time complexity is still
|
|
|
|
|
$O(nm)$ and it is possible to create inputs
|
|
|
|
|
that make the algorithm as slow as the
|
2017-02-17 21:13:30 +01:00
|
|
|
|
original Bellman–Ford algorithm.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:36:06 +01:00
|
|
|
|
\section{Dijkstra's algorithm}
|
|
|
|
|
|
|
|
|
|
\index{Dijkstra's algorithm}
|
|
|
|
|
|
2017-02-26 12:51:38 +01:00
|
|
|
|
\key{Dijkstra's algorithm}\footnote{E. W. Dijkstra published the algorithm in 1959 \cite{dij59};
|
|
|
|
|
however, his original paper does not mention how to implement the algorithm efficiently.}
|
|
|
|
|
finds the shortest
|
2017-01-07 19:36:06 +01:00
|
|
|
|
paths from the starting node to all other nodes,
|
|
|
|
|
like the Bellman–Ford algorithm.
|
|
|
|
|
The benefit in Dijsktra's algorithm is that
|
|
|
|
|
it is more efficient and can be used for
|
|
|
|
|
processing large graphs.
|
|
|
|
|
However, the algorithm requires that there
|
|
|
|
|
are no negative weight edges in the graph.
|
|
|
|
|
|
|
|
|
|
Like the Bellman–Ford algorithm,
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Dijkstra's algorithm maintains distances
|
2017-02-17 21:13:30 +01:00
|
|
|
|
to the nodes and reduces them during the search.
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Dijkstra's algorithm is efficient, because
|
2017-01-07 19:36:06 +01:00
|
|
|
|
it only processes
|
|
|
|
|
each edge in the graph once, using the fact
|
|
|
|
|
that there are no negative edges.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:36:06 +01:00
|
|
|
|
\subsubsection{Example}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Let us consider how Dijkstra's algorithm
|
2017-01-07 19:36:06 +01:00
|
|
|
|
works in the following graph when the
|
|
|
|
|
starting node is node 1:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {3};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {4};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {2};
|
|
|
|
|
\node[draw, circle] (4) at (4,1) {1};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {5};
|
|
|
|
|
|
|
|
|
|
\node[color=red] at (1,3+0.6) {$\infty$};
|
|
|
|
|
\node[color=red] at (4,3+0.6) {$\infty$};
|
|
|
|
|
\node[color=red] at (1,1-0.6) {$\infty$};
|
|
|
|
|
\node[color=red] at (4,1-0.6) {$0$};
|
|
|
|
|
\node[color=red] at (6,2-0.6) {$\infty$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:6] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
2017-01-07 19:36:06 +01:00
|
|
|
|
Like in the Bellman–Ford algorithm,
|
2017-02-17 21:13:30 +01:00
|
|
|
|
initially the distance to the starting node is 0
|
2017-02-05 10:51:38 +01:00
|
|
|
|
and the distance to all other nodes is infinite.
|
2017-01-07 19:36:06 +01:00
|
|
|
|
|
|
|
|
|
At each step, Dijkstra's algorithm selects a node
|
2017-02-05 10:51:38 +01:00
|
|
|
|
that has not been processed yet and whose distance
|
2017-01-07 19:36:06 +01:00
|
|
|
|
is as small as possible.
|
|
|
|
|
The first such node is node 1 with distance 0.
|
|
|
|
|
|
|
|
|
|
When a node is selected, the algorithm
|
2017-02-05 10:51:38 +01:00
|
|
|
|
goes through all edges that start at the node
|
|
|
|
|
and reduces the distances using them:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {3};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {4};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {2};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (4) at (4,1) {1};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {5};
|
|
|
|
|
|
|
|
|
|
\node[color=red] at (1,3+0.6) {$\infty$};
|
|
|
|
|
\node[color=red] at (4,3+0.6) {$9$};
|
|
|
|
|
\node[color=red] at (1,1-0.6) {$5$};
|
|
|
|
|
\node[color=red] at (4,1-0.6) {$0$};
|
|
|
|
|
\node[color=red] at (6,2-0.6) {$1$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:6] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (4) -- (2);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (4) -- (3);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (4) -- (5);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
2017-02-05 10:51:38 +01:00
|
|
|
|
The edges from node 1 reduced distances to
|
|
|
|
|
nodes 2, 4 and 5, whose distances are now 5, 9 and 1.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:36:06 +01:00
|
|
|
|
The next node to be processed is node 5 with distance 1:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {3};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {4};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {2};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (4) at (4,1) {1};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (5) at (6,2) {5};
|
|
|
|
|
|
|
|
|
|
\node[color=red] at (1,3+0.6) {$\infty$};
|
|
|
|
|
\node[color=red] at (4,3+0.6) {$3$};
|
|
|
|
|
\node[color=red] at (1,1-0.6) {$5$};
|
|
|
|
|
\node[color=red] at (4,1-0.6) {$0$};
|
|
|
|
|
\node[color=red] at (6,2-0.6) {$1$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:6] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (5) -- (2);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
2017-01-07 19:36:06 +01:00
|
|
|
|
After this, the next node is node 4:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {3};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (2) at (4,3) {4};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {2};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (4) at (4,1) {1};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (5) at (6,2) {5};
|
|
|
|
|
|
|
|
|
|
\node[color=red] at (1,3+0.6) {$9$};
|
|
|
|
|
\node[color=red] at (4,3+0.6) {$3$};
|
|
|
|
|
\node[color=red] at (1,1-0.6) {$5$};
|
|
|
|
|
\node[color=red] at (4,1-0.6) {$0$};
|
|
|
|
|
\node[color=red] at (6,2-0.6) {$1$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:6] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (2) -- (1);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
A remarkable property in Dijkstra's algorithm is that
|
2017-01-07 19:36:06 +01:00
|
|
|
|
whenever a node is selected, its distance is final.
|
|
|
|
|
For example, at this point of the algorithm,
|
|
|
|
|
the distances 0, 1 and 3 are the final distances
|
|
|
|
|
to nodes 1, 5 and 4.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:36:06 +01:00
|
|
|
|
After this, the algorithm processes the two
|
|
|
|
|
remaining nodes, and the final distances are as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
|
\node[draw, circle, fill=lightgray] (1) at (1,3) {3};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (2) at (4,3) {4};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (3) at (1,1) {2};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (4) at (4,1) {1};
|
|
|
|
|
\node[draw, circle, fill=lightgray] (5) at (6,2) {5};
|
|
|
|
|
|
|
|
|
|
\node[color=red] at (1,3+0.6) {$7$};
|
|
|
|
|
\node[color=red] at (4,3+0.6) {$3$};
|
|
|
|
|
\node[color=red] at (1,1-0.6) {$5$};
|
|
|
|
|
\node[color=red] at (4,1-0.6) {$0$};
|
|
|
|
|
\node[color=red] at (6,2-0.6) {$1$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:6] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
|
|
|
|
|
2017-01-07 19:36:06 +01:00
|
|
|
|
\subsubsection{Negative edges}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:36:06 +01:00
|
|
|
|
The efficiency of Dijkstra's algorithm is
|
2017-02-05 10:51:38 +01:00
|
|
|
|
based on the fact that the graph does not
|
2017-01-07 19:36:06 +01:00
|
|
|
|
contain negative edges.
|
|
|
|
|
If there is a negative edge,
|
|
|
|
|
the algorithm may give incorrect results.
|
|
|
|
|
As an example, consider the following graph:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
|
\node[draw, circle] (1) at (0,0) {$1$};
|
|
|
|
|
\node[draw, circle] (2) at (2,1) {$2$};
|
|
|
|
|
\node[draw, circle] (3) at (2,-1) {$3$};
|
|
|
|
|
\node[draw, circle] (4) at (4,0) {$4$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:2] {} (2);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:3] {} (4);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=below:6] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:$-5$] {} (4);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
|
|
|
|
\noindent
|
2017-01-07 19:36:06 +01:00
|
|
|
|
The shortest path from node 1 to node 4 is
|
2017-02-05 10:51:38 +01:00
|
|
|
|
$1 \rightarrow 3 \rightarrow 4$
|
2017-01-07 19:36:06 +01:00
|
|
|
|
and its length is 1.
|
|
|
|
|
However, Dijkstra's algorithm
|
|
|
|
|
finds the path $1 \rightarrow 2 \rightarrow 4$
|
2017-02-05 10:51:38 +01:00
|
|
|
|
by following the minimum weight edges.
|
|
|
|
|
The algorithm does not take into account that
|
2017-02-17 21:13:30 +01:00
|
|
|
|
on the other path, the weight $-5$
|
2017-01-07 19:36:06 +01:00
|
|
|
|
compensates the previous large weight $6$.
|
|
|
|
|
|
|
|
|
|
\subsubsection{Implementation}
|
|
|
|
|
|
|
|
|
|
The following implementation of Dijkstra's algorithm
|
2017-02-05 10:51:38 +01:00
|
|
|
|
calculates the minimum distances from a node $x$
|
2017-01-07 19:36:06 +01:00
|
|
|
|
to all other nodes.
|
2017-04-17 14:27:43 +02:00
|
|
|
|
The graph is stored as adjacency lists
|
|
|
|
|
so that \texttt{adj[$a$]} contains a pair $(b,w)$
|
|
|
|
|
always when there is an edge from node $a$ to node $b$
|
|
|
|
|
with weight $w$.
|
2017-01-07 19:36:06 +01:00
|
|
|
|
|
|
|
|
|
An efficient implementation of Dijkstra's algorithm
|
2017-02-05 10:51:38 +01:00
|
|
|
|
requires that it is possible to efficiently find the
|
|
|
|
|
minimum distance node that has not been processed.
|
|
|
|
|
An appropriate data structure for this is a priority queue
|
|
|
|
|
that contains the nodes ordered by their distances.
|
2017-01-07 19:36:06 +01:00
|
|
|
|
Using a priority queue, the next node to be processed
|
|
|
|
|
can be retrieved in logarithmic time.
|
|
|
|
|
|
2017-04-17 14:27:43 +02:00
|
|
|
|
The following implementation uses a priority queue
|
|
|
|
|
\texttt{q} that contains pairs of the form $(-d,x)$:
|
|
|
|
|
the current distance to node $x$ is $d$.
|
|
|
|
|
The array $\texttt{dist}$ contains the distance to
|
|
|
|
|
each node, and the array $\texttt{ready}$ indicates
|
|
|
|
|
whether a node has been processed.
|
|
|
|
|
Initially the distance to $0$ to $x$ and $\infty$ to all other nodes.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
2017-04-17 14:27:43 +02:00
|
|
|
|
for (int i = 1; i <= n; i++) dist[i] = INF;
|
|
|
|
|
dist[x] = 0;
|
2016-12-28 23:54:51 +01:00
|
|
|
|
q.push({0,x});
|
|
|
|
|
while (!q.empty()) {
|
|
|
|
|
int a = q.top().second; q.pop();
|
2017-04-17 12:58:04 +02:00
|
|
|
|
if (ready[a]) continue;
|
|
|
|
|
ready[a] = true;
|
2017-04-17 14:27:43 +02:00
|
|
|
|
for (auto u : v[a]) {
|
|
|
|
|
int b = u.first, w = u.second;
|
|
|
|
|
if (dist[a]+w < dist[b]) {
|
|
|
|
|
dist[b] = dist[a]+w;
|
|
|
|
|
q.push({-dist[b],b});
|
2016-12-28 23:54:51 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
2017-04-17 14:27:43 +02:00
|
|
|
|
Note that the priority queue contains \emph{negative}
|
|
|
|
|
distances to nodes.
|
2017-04-23 12:24:13 +02:00
|
|
|
|
The reason for this is that the C++ priority queue finds maximum
|
|
|
|
|
elements by default while we want to find minimum elements.
|
2017-04-17 14:27:43 +02:00
|
|
|
|
By using negative distances,
|
|
|
|
|
we can directly use the default version of the C++ priority queue\footnote{Of
|
|
|
|
|
course, we could also declare the priority queue as in Chapter 4.5
|
|
|
|
|
and use positive distances, but the implementation would be a bit longer.}.
|
2017-04-23 12:24:13 +02:00
|
|
|
|
Also note that there may be several instances of the same
|
|
|
|
|
node in the priority queue; however, only the instance with the
|
|
|
|
|
minimum distance will be processed.
|
2017-04-17 14:27:43 +02:00
|
|
|
|
|
2017-01-07 19:36:06 +01:00
|
|
|
|
The time complexity of the above implementation is
|
|
|
|
|
$O(n+m \log m)$ because the algorithm goes through
|
2017-02-17 21:13:30 +01:00
|
|
|
|
all nodes in the graph and adds for each edge
|
|
|
|
|
at most one distance to the priority queue.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:50:41 +01:00
|
|
|
|
\section{Floyd–Warshall algorithm}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:50:41 +01:00
|
|
|
|
\index{Floyd–Warshall algorithm}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-02-26 12:51:38 +01:00
|
|
|
|
The \key{Floyd–Warshall algorithm}\footnote{The algorithm
|
|
|
|
|
is named after R. W. Floyd and S. Warshall
|
|
|
|
|
who published it independently in 1962 \cite{flo62,war62}.}
|
2017-01-07 19:50:41 +01:00
|
|
|
|
is an alternative way to approach the problem
|
|
|
|
|
of finding shortest paths.
|
2017-04-17 11:18:29 +02:00
|
|
|
|
Unlike the other algorithms in this chapter,
|
2017-01-07 19:50:41 +01:00
|
|
|
|
it finds all shortest paths between the nodes
|
|
|
|
|
in a single run.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:50:41 +01:00
|
|
|
|
The algorithm maintains a two-dimensional array
|
|
|
|
|
that contains distances between the nodes.
|
|
|
|
|
First, the distances are calculated only using
|
|
|
|
|
direct edges between the nodes.
|
2017-02-17 21:13:30 +01:00
|
|
|
|
After this the algorithm reduces the distances
|
2017-02-05 10:51:38 +01:00
|
|
|
|
by using intermediate nodes in the paths.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:50:41 +01:00
|
|
|
|
\subsubsection{Example}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
Let us consider how the Floyd–Warshall algorithm
|
2017-01-07 19:50:41 +01:00
|
|
|
|
works in the following graph:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {$3$};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {$4$};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {$2$};
|
|
|
|
|
\node[draw, circle] (4) at (4,1) {$1$};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {$5$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
|
|
|
|
|
2017-01-07 19:50:41 +01:00
|
|
|
|
Initially, the distance from each node to itself is $0$,
|
|
|
|
|
and the distance between nodes $a$ and $b$ is $x$
|
|
|
|
|
if there is an edge between nodes $a$ and $b$ with weight $x$.
|
|
|
|
|
All other distances are infinite.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-01-07 19:50:41 +01:00
|
|
|
|
In this graph, the initial array is as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tabular}{r|rrrrr}
|
|
|
|
|
& 1 & 2 & 3 & 4 & 5 \\
|
|
|
|
|
\hline
|
|
|
|
|
1 & 0 & 5 & $\infty$ & 9 & 1 \\
|
|
|
|
|
2 & 5 & 0 & 2 & $\infty$ & $\infty$ \\
|
|
|
|
|
3 & $\infty$ & 2 & 0 & 7 & $\infty$ \\
|
|
|
|
|
4 & 9 & $\infty$ & 7 & 0 & 2 \\
|
|
|
|
|
5 & 1 & $\infty$ & $\infty$ & 2 & 0 \\
|
|
|
|
|
\end{tabular}
|
|
|
|
|
\end{center}
|
|
|
|
|
\vspace{10pt}
|
2017-02-05 10:51:38 +01:00
|
|
|
|
The algorithm consists of consecutive rounds.
|
|
|
|
|
On each round, the algorithm selects a new node
|
|
|
|
|
that can act as an intermediate node in paths from now on,
|
|
|
|
|
and the algorithm reduces the distances in the array
|
2017-01-07 19:50:41 +01:00
|
|
|
|
using this node.
|
|
|
|
|
|
2017-02-17 21:13:30 +01:00
|
|
|
|
On the first round, node 1 is the new intermediate node.
|
2017-02-05 10:51:38 +01:00
|
|
|
|
There is a new path between nodes 2 and 4
|
|
|
|
|
with length 14, because node 1 connects them.
|
|
|
|
|
There is also a new path
|
2017-01-07 19:50:41 +01:00
|
|
|
|
between nodes 2 and 5 with length 6.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tabular}{r|rrrrr}
|
|
|
|
|
& 1 & 2 & 3 & 4 & 5 \\
|
|
|
|
|
\hline
|
|
|
|
|
1 & 0 & 5 & $\infty$ & 9 & 1 \\
|
|
|
|
|
2 & 5 & 0 & 2 & \textbf{14} & \textbf{6} \\
|
|
|
|
|
3 & $\infty$ & 2 & 0 & 7 & $\infty$ \\
|
|
|
|
|
4 & 9 & \textbf{14} & 7 & 0 & 2 \\
|
|
|
|
|
5 & 1 & \textbf{6} & $\infty$ & 2 & 0 \\
|
|
|
|
|
\end{tabular}
|
|
|
|
|
\end{center}
|
|
|
|
|
\vspace{10pt}
|
|
|
|
|
|
2017-02-17 21:13:30 +01:00
|
|
|
|
On the second round, node 2 is the new intermediate node.
|
2017-02-05 10:51:38 +01:00
|
|
|
|
This creates new paths between nodes 1 and 3
|
2017-01-07 19:50:41 +01:00
|
|
|
|
and between nodes 3 and 5:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tabular}{r|rrrrr}
|
|
|
|
|
& 1 & 2 & 3 & 4 & 5 \\
|
|
|
|
|
\hline
|
|
|
|
|
1 & 0 & 5 & \textbf{7} & 9 & 1 \\
|
|
|
|
|
2 & 5 & 0 & 2 & 14 & 6 \\
|
|
|
|
|
3 & \textbf{7} & 2 & 0 & 7 & \textbf{8} \\
|
|
|
|
|
4 & 9 & 14 & 7 & 0 & 2 \\
|
|
|
|
|
5 & 1 & 6 & \textbf{8} & 2 & 0 \\
|
|
|
|
|
\end{tabular}
|
|
|
|
|
\end{center}
|
|
|
|
|
\vspace{10pt}
|
|
|
|
|
|
2017-02-17 21:13:30 +01:00
|
|
|
|
On the third round, node 3 is the new intermediate round.
|
2017-01-07 19:50:41 +01:00
|
|
|
|
There is a new path between nodes 2 and 4:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tabular}{r|rrrrr}
|
|
|
|
|
& 1 & 2 & 3 & 4 & 5 \\
|
|
|
|
|
\hline
|
|
|
|
|
1 & 0 & 5 & 7 & 9 & 1 \\
|
|
|
|
|
2 & 5 & 0 & 2 & \textbf{9} & 6 \\
|
|
|
|
|
3 & 7 & 2 & 0 & 7 & 8 \\
|
|
|
|
|
4 & 9 & \textbf{9} & 7 & 0 & 2 \\
|
|
|
|
|
5 & 1 & 6 & 8 & 2 & 0 \\
|
|
|
|
|
\end{tabular}
|
|
|
|
|
\end{center}
|
|
|
|
|
\vspace{10pt}
|
|
|
|
|
|
2017-01-07 19:50:41 +01:00
|
|
|
|
The algorithm continues like this,
|
2017-02-17 21:13:30 +01:00
|
|
|
|
until all nodes have been appointed intermediate nodes.
|
2017-01-07 19:50:41 +01:00
|
|
|
|
After the algorithm has finished, the array contains
|
2017-02-05 10:51:38 +01:00
|
|
|
|
the minimum distances between any two nodes:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tabular}{r|rrrrr}
|
|
|
|
|
& 1 & 2 & 3 & 4 & 5 \\
|
|
|
|
|
\hline
|
|
|
|
|
1 & 0 & 5 & 7 & 3 & 1 \\
|
2017-04-24 20:25:58 +02:00
|
|
|
|
2 & 5 & 0 & 2 & 8 & 6 \\
|
2016-12-28 23:54:51 +01:00
|
|
|
|
3 & 7 & 2 & 0 & 7 & 8 \\
|
2017-04-24 20:25:58 +02:00
|
|
|
|
4 & 3 & 8 & 7 & 0 & 2 \\
|
2016-12-28 23:54:51 +01:00
|
|
|
|
5 & 1 & 6 & 8 & 2 & 0 \\
|
|
|
|
|
\end{tabular}
|
|
|
|
|
\end{center}
|
|
|
|
|
|
2017-02-17 21:13:30 +01:00
|
|
|
|
For example, the array tells us that the
|
|
|
|
|
shortest distance between nodes 2 and 4 is 8.
|
2017-01-07 19:50:41 +01:00
|
|
|
|
This corresponds to the following path:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
|
\node[draw, circle] (1) at (1,3) {$3$};
|
|
|
|
|
\node[draw, circle] (2) at (4,3) {$4$};
|
|
|
|
|
\node[draw, circle] (3) at (1,1) {$2$};
|
|
|
|
|
\node[draw, circle] (4) at (4,1) {$1$};
|
|
|
|
|
\node[draw, circle] (5) at (6,2) {$5$};
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (2);
|
|
|
|
|
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
|
|
|
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
|
|
|
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
|
|
|
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (3) -- (4);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (4) -- (5);
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (5) -- (2);
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
|
\end{center}
|
|
|
|
|
|
2017-01-07 19:50:41 +01:00
|
|
|
|
\subsubsection{Implementation}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
The advantage of the
|
2017-01-07 19:50:41 +01:00
|
|
|
|
Floyd–Warshall algorithm that it is
|
|
|
|
|
easy to implement.
|
|
|
|
|
The following code constructs a
|
2017-04-17 14:27:43 +02:00
|
|
|
|
distance matrix where $\texttt{dist}[a][b]$
|
2017-02-17 21:13:30 +01:00
|
|
|
|
is the shortest distance between nodes $a$ and $b$.
|
2017-04-17 14:27:43 +02:00
|
|
|
|
First, the algorithm initializes \texttt{dist}
|
|
|
|
|
using the adjacency matrix \texttt{mat} of the graph:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
for (int j = 1; j <= n; j++) {
|
2017-04-17 14:27:43 +02:00
|
|
|
|
if (i == j) dist[i][j] = 0;
|
|
|
|
|
else if (mat[i][j]) dist[i][j] = mat[i][j];
|
|
|
|
|
else dist[i][j] = INF;
|
2016-12-28 23:54:51 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
2017-02-17 21:13:30 +01:00
|
|
|
|
After this, the shortest distances can be found as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
for (int k = 1; k <= n; k++) {
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
for (int j = 1; j <= n; j++) {
|
2017-04-17 14:27:43 +02:00
|
|
|
|
dist[i][j] = min(dist[i][j], dist[i][k]+dist[k][j]);
|
2016-12-28 23:54:51 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
2017-02-05 10:51:38 +01:00
|
|
|
|
The time complexity of the algorithm is $O(n^3)$,
|
2017-01-07 19:50:41 +01:00
|
|
|
|
because it contains three nested loops
|
2017-04-17 14:27:43 +02:00
|
|
|
|
that go through the nodes of the graph.
|
2017-01-07 19:50:41 +01:00
|
|
|
|
|
|
|
|
|
Since the implementation of the Floyd–Warshall
|
|
|
|
|
algorithm is simple, the algorithm can be
|
2017-02-05 10:51:38 +01:00
|
|
|
|
a good choice even if it is only needed to find a
|
2017-01-07 19:50:41 +01:00
|
|
|
|
single shortest path in the graph.
|
2017-02-05 10:51:38 +01:00
|
|
|
|
However, the algorithm can only be used when the graph
|
2017-03-06 23:57:32 +01:00
|
|
|
|
is so small that a cubic time complexity is fast enough.
|