cphb/chapter16.tex

236 lines
7.8 KiB
TeX
Raw Normal View History

2021-02-08 23:10:42 +01:00
\chapter{Topological sorting}
2016-12-28 23:54:51 +01:00
2017-01-08 15:43:57 +01:00
\index{topological sorting}
\index{cycle}
2016-12-28 23:54:51 +01:00
2017-05-07 20:18:56 +02:00
A \key{topological sort} is an ordering
2017-01-08 15:43:57 +01:00
of the nodes of a directed graph
2017-02-06 01:04:46 +01:00
such that if there is a path from node $a$ to node $b$,
then node $a$ appears before node $b$ in the ordering.
2017-01-08 15:43:57 +01:00
For example, for the graph
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tikzpicture}[scale=0.9]
\node[draw, circle] (1) at (1,5) {$1$};
\node[draw, circle] (2) at (3,5) {$2$};
\node[draw, circle] (3) at (5,5) {$3$};
\node[draw, circle] (4) at (1,3) {$4$};
\node[draw, circle] (5) at (3,3) {$5$};
\node[draw, circle] (6) at (5,3) {$6$};
\path[draw,thick,->,>=latex] (1) -- (2);
\path[draw,thick,->,>=latex] (2) -- (3);
\path[draw,thick,->,>=latex] (4) -- (1);
\path[draw,thick,->,>=latex] (4) -- (5);
\path[draw,thick,->,>=latex] (5) -- (2);
\path[draw,thick,->,>=latex] (5) -- (3);
\path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture}
\end{center}
2017-05-29 19:35:23 +02:00
one topological sort is
2016-12-28 23:54:51 +01:00
$[4,1,5,2,3,6]$:
\begin{center}
\begin{tikzpicture}[scale=0.9]
\node[draw, circle] (1) at (-6,0) {$1$};
\node[draw, circle] (2) at (-3,0) {$2$};
\node[draw, circle] (3) at (-1.5,0) {$3$};
\node[draw, circle] (4) at (-7.5,0) {$4$};
\node[draw, circle] (5) at (-4.5,0) {$5$};
\node[draw, circle] (6) at (-0,0) {$6$};
\path[draw,thick,->,>=latex] (1) edge [bend right=30] (2);
\path[draw,thick,->,>=latex] (2) -- (3);
\path[draw,thick,->,>=latex] (4) -- (1);
\path[draw,thick,->,>=latex] (4) edge [bend left=30] (5);
\path[draw,thick,->,>=latex] (5) -- (2);
\path[draw,thick,->,>=latex] (5) edge [bend left=30] (3);
\path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture}
\end{center}
2017-02-06 01:04:46 +01:00
An acyclic graph always has a topological sort.
2017-01-08 15:43:57 +01:00
However, if the graph contains a cycle,
2017-02-06 01:04:46 +01:00
it is not possible to form a topological sort,
2017-05-07 20:18:56 +02:00
because no node of the cycle can appear
before the other nodes of the cycle in the ordering.
2017-02-06 01:04:46 +01:00
It turns out that depth-first search can be used
to both check if a directed graph contains a cycle
and, if it does not contain a cycle, to construct a topological sort.
2016-12-28 23:54:51 +01:00
2017-01-08 15:43:57 +01:00
\subsubsection{Algorithm}
2016-12-28 23:54:51 +01:00
2017-02-17 21:45:59 +01:00
The idea is to go through the nodes of the graph
2017-02-06 01:04:46 +01:00
and always begin a depth-first search at the current node
if it has not been processed yet.
During the searches, the nodes have three possible states:
2016-12-28 23:54:51 +01:00
\begin{itemize}
2017-01-08 15:43:57 +01:00
\item state 0: the node has not been processed (white)
\item state 1: the node is under processing (light gray)
\item state 2: the node has been processed (dark gray)
2016-12-28 23:54:51 +01:00
\end{itemize}
2017-01-08 15:43:57 +01:00
Initially, the state of each node is 0.
When a search reaches a node for the first time,
2017-02-06 01:04:46 +01:00
its state becomes 1.
Finally, after all successors of the node have
2017-02-17 21:45:59 +01:00
been processed, its state becomes 2.
2016-12-28 23:54:51 +01:00
2017-05-07 20:18:56 +02:00
If the graph contains a cycle, we will find this out
2017-02-06 01:04:46 +01:00
during the search, because sooner or later
2017-01-08 15:43:57 +01:00
we will arrive at a node whose state is 1.
In this case, it is not possible to construct a topological sort.
2016-12-28 23:54:51 +01:00
2017-02-06 01:04:46 +01:00
If the graph does not contain a cycle, we can construct
2017-02-17 21:45:59 +01:00
a topological sort by
adding each node to a list when the state of the node becomes 2.
2017-01-08 15:43:57 +01:00
This list in reverse order is a topological sort.
2016-12-28 23:54:51 +01:00
2017-01-08 15:43:57 +01:00
\subsubsection{Example 1}
2016-12-28 23:54:51 +01:00
2017-01-08 15:43:57 +01:00
In the example graph, the search first proceeds
from node 1 to node 6:
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tikzpicture}[scale=0.9]
\node[draw, circle,fill=gray!20] (1) at (1,5) {$1$};
\node[draw, circle,fill=gray!20] (2) at (3,5) {$2$};
\node[draw, circle,fill=gray!20] (3) at (5,5) {$3$};
\node[draw, circle] (4) at (1,3) {$4$};
\node[draw, circle] (5) at (3,3) {$5$};
\node[draw, circle,fill=gray!80] (6) at (5,3) {$6$};
\path[draw,thick,->,>=latex] (4) -- (1);
\path[draw,thick,->,>=latex] (4) -- (5);
\path[draw,thick,->,>=latex] (5) -- (2);
\path[draw,thick,->,>=latex] (5) -- (3);
%\path[draw,thick,->,>=latex] (3) -- (6);
\path[draw=red,thick,->,line width=2pt] (1) -- (2);
\path[draw=red,thick,->,line width=2pt] (2) -- (3);
\path[draw=red,thick,->,line width=2pt] (3) -- (6);
\end{tikzpicture}
\end{center}
2017-01-08 15:43:57 +01:00
Now node 6 has been processed, so it is added to the list.
2017-02-06 01:04:46 +01:00
After this, also nodes 3, 2 and 1 are added to the list:
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tikzpicture}[scale=0.9]
\node[draw, circle,fill=gray!80] (1) at (1,5) {$1$};
\node[draw, circle,fill=gray!80] (2) at (3,5) {$2$};
\node[draw, circle,fill=gray!80] (3) at (5,5) {$3$};
\node[draw, circle] (4) at (1,3) {$4$};
\node[draw, circle] (5) at (3,3) {$5$};
\node[draw, circle,fill=gray!80] (6) at (5,3) {$6$};
\path[draw,thick,->,>=latex] (1) -- (2);
\path[draw,thick,->,>=latex] (2) -- (3);
\path[draw,thick,->,>=latex] (4) -- (1);
\path[draw,thick,->,>=latex] (4) -- (5);
\path[draw,thick,->,>=latex] (5) -- (2);
\path[draw,thick,->,>=latex] (5) -- (3);
\path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture}
\end{center}
2017-02-06 01:04:46 +01:00
At this point, the list is $[6,3,2,1]$.
2017-01-08 15:43:57 +01:00
The next search begins at node 4:
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tikzpicture}[scale=0.9]
\node[draw, circle,fill=gray!80] (1) at (1,5) {$1$};
\node[draw, circle,fill=gray!80] (2) at (3,5) {$2$};
\node[draw, circle,fill=gray!80] (3) at (5,5) {$3$};
\node[draw, circle,fill=gray!20] (4) at (1,3) {$4$};
\node[draw, circle,fill=gray!80] (5) at (3,3) {$5$};
\node[draw, circle,fill=gray!80] (6) at (5,3) {$6$};
\path[draw,thick,->,>=latex] (1) -- (2);
\path[draw,thick,->,>=latex] (2) -- (3);
\path[draw,thick,->,>=latex] (4) -- (1);
%\path[draw,thick,->,>=latex] (4) -- (5);
\path[draw,thick,->,>=latex] (5) -- (2);
\path[draw,thick,->,>=latex] (5) -- (3);
\path[draw,thick,->,>=latex] (3) -- (6);
\path[draw=red,thick,->,line width=2pt] (4) -- (5);
\end{tikzpicture}
\end{center}
2017-01-08 15:43:57 +01:00
Thus, the final list is $[6,3,2,1,5,4]$.
We have processed all nodes, so a topological sort has
been found.
The topological sort is the reverse list
$[4,5,1,2,3,6]$:
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tikzpicture}[scale=0.9]
\node[draw, circle] (1) at (3,0) {$1$};
\node[draw, circle] (2) at (4.5,0) {$2$};
\node[draw, circle] (3) at (6,0) {$3$};
\node[draw, circle] (4) at (0,0) {$4$};
\node[draw, circle] (5) at (1.5,0) {$5$};
\node[draw, circle] (6) at (7.5,0) {$6$};
\path[draw,thick,->,>=latex] (1) -- (2);
\path[draw,thick,->,>=latex] (2) -- (3);
\path[draw,thick,->,>=latex] (4) edge [bend left=30] (1);
\path[draw,thick,->,>=latex] (4) -- (5);
\path[draw,thick,->,>=latex] (5) edge [bend right=30] (2);
\path[draw,thick,->,>=latex] (5) edge [bend right=40] (3);
\path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture}
\end{center}
2017-01-08 15:43:57 +01:00
Note that a topological sort is not unique,
2017-05-07 20:18:56 +02:00
and there can be several topological sorts for a graph.
2016-12-28 23:54:51 +01:00
2017-01-08 15:43:57 +01:00
\subsubsection{Example 2}
2016-12-28 23:54:51 +01:00
2017-02-06 01:04:46 +01:00
Let us now consider a graph for which we
cannot construct a topological sort,
2017-05-29 19:35:23 +02:00
because the graph contains a cycle:
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tikzpicture}[scale=0.9]
\node[draw, circle] (1) at (1,5) {$1$};
\node[draw, circle] (2) at (3,5) {$2$};
\node[draw, circle] (3) at (5,5) {$3$};
\node[draw, circle] (4) at (1,3) {$4$};
\node[draw, circle] (5) at (3,3) {$5$};
\node[draw, circle] (6) at (5,3) {$6$};
\path[draw,thick,->,>=latex] (1) -- (2);
\path[draw,thick,->,>=latex] (2) -- (3);
\path[draw,thick,->,>=latex] (4) -- (1);
\path[draw,thick,->,>=latex] (4) -- (5);
\path[draw,thick,->,>=latex] (5) -- (2);
\path[draw,thick,->,>=latex] (3) -- (5);
\path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture}
\end{center}
2017-02-06 01:04:46 +01:00
The search proceeds as follows:
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tikzpicture}[scale=0.9]
\node[draw, circle,fill=gray!20] (1) at (1,5) {$1$};
\node[draw, circle,fill=gray!20] (2) at (3,5) {$2$};
\node[draw, circle,fill=gray!20] (3) at (5,5) {$3$};
\node[draw, circle] (4) at (1,3) {$4$};
\node[draw, circle,fill=gray!20] (5) at (3,3) {$5$};
\node[draw, circle] (6) at (5,3) {$6$};
\path[draw,thick,->,>=latex] (4) -- (1);
\path[draw,thick,->,>=latex] (4) -- (5);
\path[draw,thick,->,>=latex] (3) -- (6);
\path[draw=red,thick,->,line width=2pt] (1) -- (2);
\path[draw=red,thick,->,line width=2pt] (2) -- (3);
\path[draw=red,thick,->,line width=2pt] (3) -- (5);
\path[draw=red,thick,->,line width=2pt] (5) -- (2);
\end{tikzpicture}
\end{center}
2017-02-06 01:04:46 +01:00
The search reaches node 2 whose state is 1,
2017-05-29 19:35:23 +02:00
which means that the graph contains a cycle.
2017-05-07 20:18:56 +02:00
In this example, there is a cycle
$2 \rightarrow 3 \rightarrow 5 \rightarrow 2$.