cphb/chapter13.tex

801 lines
27 KiB
TeX
Raw Normal View History

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{BellmanFord algorithm}
\index{BellmanFord algorithm}
2017-02-26 12:51:38 +01:00
The \key{BellmanFord 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 BellmanFord 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
BellmanFord 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-02-05 10:51:38 +01:00
as adjacency lists in an array
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
vector<pair<int,int>> v[N];
\end{lstlisting}
2017-02-05 10:51:38 +01:00
as pairs of the form $(x,w)$:
there is an edge to node $x$ 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-02-17 21:13:30 +01:00
The algorithm constructs an array \texttt{e}
2017-01-07 19:08:47 +01:00
that will contain the distance from $x$
to all nodes in the graph.
The initial value $10^9$ means infinity.
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9;
e[x] = 0;
for (int i = 1; i <= n-1; i++) {
for (int a = 1; a <= n; a++) {
for (auto b : v[a]) {
e[b.first] = min(e[b.first],e[a]+b.second);
}
}
}
\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-01-07 19:08:47 +01:00
\subsubsection{Negative cycle}
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 BellmanFord 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 BellmanFord 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 BellmanFord 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-01-07 19:08:47 +01:00
2017-02-17 21:13:30 +01:00
The following implementation uses a
\texttt{queue} \texttt{q}.
In addition, an array \texttt{z} indicates
2017-01-07 19:08:47 +01:00
if a node is already in the queue,
2017-02-05 10:51:38 +01:00
in which case the algorithm does not add
2017-01-07 19:08:47 +01:00
the node to the queue again.
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9;
e[x] = 0;
q.push(x);
while (!q.empty()) {
int a = q.front(); q.pop();
z[a] = 0;
for (auto b : v[a]) {
if (e[a]+b.second < e[b.first]) {
e[b.first] = e[a]+b.second;
if (!z[b]) {q.push(b); z[b] = 1;}
}
}
}
\end{lstlisting}
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 BellmanFord 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 BellmanFord 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 BellmanFord 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 BellmanFord 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.
The graph is stored in an array \texttt{v}
2017-02-05 10:51:38 +01:00
as adjacency lists like in the BellmanFord algorithm.
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.
In the following implementation,
the priority queue contains pairs whose first
2017-02-17 21:13:30 +01:00
element is the current distance to the node and second
2017-02-05 10:51:38 +01:00
element is the identifier of the node.
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
priority_queue<pair<int,int>> q;
\end{lstlisting}
2017-01-07 19:36:06 +01:00
A small difficulty is that in Dijkstra's algorithm,
2017-02-05 10:51:38 +01:00
we should find the node with the \emph{minimum} distance,
2017-01-07 19:36:06 +01:00
while the C++ priority queue finds the \emph{maximum}
element as default.
2017-02-17 21:13:30 +01:00
An easy trick is to use \emph{negative} distances,
2017-02-05 10:51:38 +01:00
which allows us to directly use the C++ priority queue.
2017-01-07 19:36:06 +01:00
The code keeps track of processed nodes
2017-02-17 21:13:30 +01:00
in an array \texttt{z},
and maintains the distances in an array \texttt{e}.
2017-01-07 19:36:06 +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 $10^9$ (infinite).
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9;
e[x] = 0;
q.push({0,x});
while (!q.empty()) {
int a = q.top().second; q.pop();
if (z[a]) continue;
z[a] = 1;
for (auto b : v[a]) {
2017-03-06 23:57:32 +01:00
if (e[a]+b.second < e[b.first]) {
e[b.first] = e[a]+b.second;
q.push({-e[b.first],b.first});
2016-12-28 23:54:51 +01:00
}
}
}
\end{lstlisting}
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{FloydWarshall algorithm}
2016-12-28 23:54:51 +01:00
2017-01-07 19:50:41 +01:00
\index{FloydWarshall algorithm}
2016-12-28 23:54:51 +01:00
2017-02-26 12:51:38 +01:00
The \key{FloydWarshall 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-02-05 10:51:38 +01:00
Unlike the other algorihms 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 FloydWarshall 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 \\
2 & 5 & 0 & 2 & 9 & 6 \\
3 & 7 & 2 & 0 & 7 & 8 \\
4 & 3 & 9 & 7 & 0 & 2 \\
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
FloydWarshall algorithm that it is
easy to implement.
The following code constructs a
distance matrix \texttt{d} where $\texttt{d}[a][b]$
2017-02-17 21:13:30 +01:00
is the shortest distance between nodes $a$ and $b$.
2017-01-07 19:50:41 +01:00
First, the algorithm initializes \texttt{d}
using the adjacency matrix \texttt{v} of the graph
2017-02-05 10:51:38 +01:00
($10^9$ means infinity):
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) d[i][j] = 0;
else if (v[i][j]) d[i][j] = v[i][j];
else d[i][j] = 1e9;
}
}
\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++) {
d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
}
}
}
\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
that go through the nodes in the graph.
Since the implementation of the FloydWarshall
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.