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$.
|