\chapter{Directed graphs} In this chapter, we focus on two classes of directed graphs: \begin{itemize} \item \key{Acyclic graph}: There are no cycles in the graph, so there is no path from any node to itself. \item \key{Successor graph}: The outdegree of each node is 1, so each node has a unique successor. \end{itemize} It turns out that in both cases, we can design efficient algorithms that are based on the special properties of the graphs. \section{Topological sorting} \index{topological sorting} \index{cycle} A \key{topological sort} is a ordering of the nodes of a directed graph where node $a$ is always before node $b$ if there is a path from node $a$ to node $b$. For example, for the graph \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} a possible topological sort is $[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} A topological sort always exists if the graph is acyclic. However, if the graph contains a cycle, it is not possible to find a topological sort because no node in the cycle can appear before other nodes in the cycle in the ordering. It turns out that we can use depth-first search to both construct a topological sort or find out that it is not possible because the graph contains a cycle. \subsubsection{Algorithm} The idea is to go through the nodes in the graph and always begin a depth-first search if the node has not been processed yet. During each search, the nodes have three possible states: \begin{itemize} \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) \end{itemize} Initially, the state of each node is 0. When a search reaches a node for the first time, the state of the node becomes 1. Finally, after all neighbors of a node have been processed, the state of the node becomes 2. If the graph contains a cycle, we will realize this during the search because sooner or later we will arrive at a node whose state is 1. In this case, it is not possible to construct a topological sort. If the graph doesn't contain a cycle, we can construct a topological sort by adding each node to the end of a list when its state becomes 2. This list in reverse order is a topological sort. \subsubsection{Example 1} In the example graph, the search first proceeds from node 1 to node 6: \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} Now node 6 has been processed, so it is added to the list. After this, the search returns back: \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} At this point, the list contains values $[6,3,2,1]$. The next search begins at node 4: \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} 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]$: \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} Note that a topological sort is not unique, but there can be several topological sorts for a graph. \subsubsection{Example 2} Let's consider another example where we can't construct a topological sort because there is a cycle in the graph: \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} Now the search proceeds as follows: \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} The search reaches node 2 whose state is 1 which means the graph contains a cycle. In this case, the cycle is $2 \rightarrow 3 \rightarrow 5 \rightarrow 2$. \section{Dynamic programming} If a directed graph is acyclic, dynamic programming can be applied to it. For example, we can solve the following problems concerning paths from a starting node to an ending node efficiently in $O(n+m)$ time: \begin{itemize} \item how many different paths are there? \item what is the shortest/longest path? \item what is the minimum/maximum number of edges in a path? \item which nodes certainly appear in the path? \end{itemize} \subsubsection{Counting the number of paths} As an example, let's calculate the number of paths from a starting node to an ending node in a directed, acyclic graph. For example, in the graph \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} there are 3 paths from node 4 to node 6: \begin{itemize} \item $4 \rightarrow 1 \rightarrow 2 \rightarrow 3 \rightarrow 6$ \item $4 \rightarrow 5 \rightarrow 2 \rightarrow 3 \rightarrow 6$ \item $4 \rightarrow 5 \rightarrow 3 \rightarrow 6$ \end{itemize} The idea is to go through the nodes in a topological sort, and calculate for each node the total number of paths that reach the node from different directions. In this case, the topological sort is as follows: \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} The numbers of paths are as follows: \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); \node[color=red] at (1,2.3) {$1$}; \node[color=red] at (3,2.3) {$1$}; \node[color=red] at (5,2.3) {$3$}; \node[color=red] at (1,5.7) {$1$}; \node[color=red] at (3,5.7) {$2$}; \node[color=red] at (5,5.7) {$3$}; \end{tikzpicture} \end{center} For example, there is an edge to node 2 from nodes 1 and 5. There is one path from node 4 to both node 1 and 5, so there are two paths from node 4 to node 2. Correspondingly, there is an edge to node 3 from nodes 2 and 5 that correspond to two and one paths from node 4. \subsubsection{Extending Dijkstra's algorithm} \index{Dijkstra's algorithm} A by-product of Dijkstra's algorithm is a directed, acyclic graph that shows for each node in the original graph the possible ways to reach the node using a shortest path from the starting node. Dynamic programming can be applied also to this graph. For example, in the graph \begin{center} \begin{tikzpicture} \node[draw, circle] (1) at (0,0) {$1$}; \node[draw, circle] (2) at (2,0) {$2$}; \node[draw, circle] (3) at (0,-2) {$3$}; \node[draw, circle] (4) at (2,-2) {$4$}; \node[draw, circle] (5) at (4,-1) {$5$}; \path[draw,thick,-] (1) -- node[font=\small,label=above:3] {} (2); \path[draw,thick,-] (1) -- node[font=\small,label=left:5] {} (3); \path[draw,thick,-] (2) -- node[font=\small,label=right:4] {} (4); \path[draw,thick,-] (2) -- node[font=\small,label=above:8] {} (5); \path[draw,thick,-] (3) -- node[font=\small,label=below:2] {} (4); \path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5); \path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (3); \end{tikzpicture} \end{center} the following edges can belong to the shortest paths from node 1: \begin{center} \begin{tikzpicture} \node[draw, circle] (1) at (0,0) {$1$}; \node[draw, circle] (2) at (2,0) {$2$}; \node[draw, circle] (3) at (0,-2) {$3$}; \node[draw, circle] (4) at (2,-2) {$4$}; \node[draw, circle] (5) at (4,-1) {$5$}; \path[draw,thick,->] (1) -- node[font=\small,label=above:3] {} (2); \path[draw,thick,->] (1) -- node[font=\small,label=left:5] {} (3); \path[draw,thick,->] (2) -- node[font=\small,label=right:4] {} (4); \path[draw,thick,->] (3) -- node[font=\small,label=below:2] {} (4); \path[draw,thick,->] (4) -- node[font=\small,label=below:1] {} (5); \path[draw,thick,->] (2) -- node[font=\small,label=above:2] {} (3); \end{tikzpicture} \end{center} Now we can, for example, calculate the number of shortest paths from node 1 to node 5 using dynamic programming: \begin{center} \begin{tikzpicture} \node[draw, circle] (1) at (0,0) {$1$}; \node[draw, circle] (2) at (2,0) {$2$}; \node[draw, circle] (3) at (0,-2) {$3$}; \node[draw, circle] (4) at (2,-2) {$4$}; \node[draw, circle] (5) at (4,-1) {$5$}; \path[draw,thick,->] (1) -- node[font=\small,label=above:3] {} (2); \path[draw,thick,->] (1) -- node[font=\small,label=left:5] {} (3); \path[draw,thick,->] (2) -- node[font=\small,label=right:4] {} (4); \path[draw,thick,->] (3) -- node[font=\small,label=below:2] {} (4); \path[draw,thick,->] (4) -- node[font=\small,label=below:1] {} (5); \path[draw,thick,->] (2) -- node[font=\small,label=above:2] {} (3); \node[color=red] at (0,0.7) {$1$}; \node[color=red] at (2,0.7) {$1$}; \node[color=red] at (0,-2.7) {$2$}; \node[color=red] at (2,-2.7) {$3$}; \node[color=red] at (4,-1.7) {$3$}; \end{tikzpicture} \end{center} \subsubsection{Representing problems as graphs} Actually, any dynamic programming problem can be represented as a directed, acyclic graph. In such a graph, each node is a dynamic programming state, and the edges indicate how the states depend on each other. As an example, consider the problem where our task is to form a sum of money $x$ using coins $\{c_1,c_2,\ldots,c_k\}$. In this case, we can construct a graph where each node corresponds to a sum of money, and the edges show how we can choose coins. For example, for coins $\{1,3,4\}$ and $x=6$, the graph is as follows: \begin{center} \begin{tikzpicture}[scale=0.9] \node[draw, circle] (0) at (0,0) {$0$}; \node[draw, circle] (1) at (2,0) {$1$}; \node[draw, circle] (2) at (4,0) {$2$}; \node[draw, circle] (3) at (6,0) {$3$}; \node[draw, circle] (4) at (8,0) {$4$}; \node[draw, circle] (5) at (10,0) {$5$}; \node[draw, circle] (6) at (12,0) {$6$}; \path[draw,thick,->] (0) -- (1); \path[draw,thick,->] (1) -- (2); \path[draw,thick,->] (2) -- (3); \path[draw,thick,->] (3) -- (4); \path[draw,thick,->] (4) -- (5); \path[draw,thick,->] (5) -- (6); \path[draw,thick,->] (0) edge [bend right=30] (3); \path[draw,thick,->] (1) edge [bend right=30] (4); \path[draw,thick,->] (2) edge [bend right=30] (5); \path[draw,thick,->] (3) edge [bend right=30] (6); \path[draw,thick,->] (0) edge [bend left=30] (4); \path[draw,thick,->] (1) edge [bend left=30] (5); \path[draw,thick,->] (2) edge [bend left=30] (6); \end{tikzpicture} \end{center} Using this representation, the shortest path from node 0 to node $x$ corresponds to a solution with minimum number of coins, and the total number of paths from node 0 to node $x$ equals the total number of solutions. \section{Successor paths} \index{successor graph} \index{functional graph} For the rest of the chapter, we concentrate on \key{successor graphs} where the outdegree of each node is 1, i.e., exactly one edge begins at the node. Thus, the graph consists of one or more components, and each component contains one cycle and some paths that lead to it. Successor graphs are sometimes called \key{functional graphs}. The reason for this is that any successor graph corresponds to a function $f$ that defines the edges in the graph. The parameter for the function is a node in the graph, and the function returns the successor of the node. \begin{samepage} For example, the function \begin{center} \begin{tabular}{r|rrrrrrrrr} $x$ & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\ \hline $f(x)$ & 3 & 5 & 7 & 6 & 2 & 2 & 1 & 6 & 3 \\ \end{tabular} \end{center} \end{samepage} defines the following graph: \begin{center} \begin{tikzpicture}[scale=0.9] \node[draw, circle] (1) at (0,0) {$1$}; \node[draw, circle] (2) at (2,0) {$2$}; \node[draw, circle] (3) at (-2,0) {$3$}; \node[draw, circle] (4) at (1,-3) {$4$}; \node[draw, circle] (5) at (4,0) {$5$}; \node[draw, circle] (6) at (2,-1.5) {$6$}; \node[draw, circle] (7) at (-2,-1.5) {$7$}; \node[draw, circle] (8) at (3,-3) {$8$}; \node[draw, circle] (9) at (-4,0) {$9$}; \path[draw,thick,->] (1) -- (3); \path[draw,thick,->] (2) edge [bend left=40] (5); \path[draw,thick,->] (3) -- (7); \path[draw,thick,->] (4) -- (6); \path[draw,thick,->] (5) edge [bend left=40] (2); \path[draw,thick,->] (6) -- (2); \path[draw,thick,->] (7) -- (1); \path[draw,thick,->] (8) -- (6); \path[draw,thick,->] (9) -- (3); \end{tikzpicture} \end{center} Since each node in a successor graph has a unique successor, we can define a function $f(x,k)$ that returns the node that we will reach if we begin at node $x$ and walk $k$ steps forward. For example, in the above graph $f(4,6)=2$ because by walking 6 steps from node 4, we will reach node 2: \begin{center} \begin{tikzpicture}[scale=0.9] \node[draw, circle] (1) at (0,0) {$4$}; \node[draw, circle] (2) at (1.5,0) {$6$}; \node[draw, circle] (3) at (3,0) {$2$}; \node[draw, circle] (4) at (4.5,0) {$5$}; \node[draw, circle] (5) at (6,0) {$2$}; \node[draw, circle] (6) at (7.5,0) {$5$}; \node[draw, circle] (7) at (9,0) {$2$}; \path[draw,thick,->] (1) -- (2); \path[draw,thick,->] (2) -- (3); \path[draw,thick,->] (3) -- (4); \path[draw,thick,->] (4) -- (5); \path[draw,thick,->] (5) -- (6); \path[draw,thick,->] (6) -- (7); \end{tikzpicture} \end{center} A straightforward way to calculate a value $f(x,k)$ is to walk through the path step by step which takes $O(k)$ time. However, using preprocessing, we can calculate any value $f(x,k)$ in only $O(\log k)$ time. The idea is to precalculate all values $f(x,k)$ where $k$ is a power of two and at most $u$ where $u$ is the maximum number of steps we will ever walk. This can be done efficiently because we can use the following recursion: \begin{equation*} f(x,k) = \begin{cases} f(x) & k = 1\\ f(f(x,k/2),k/2) & k > 1\\ \end{cases} \end{equation*} Precalculating values $f(x,k)$ takes $O(n \log u)$ time because we calculate $O(\log u)$ values for each node. In the above graph, the first values are as follows: \begin{center} \begin{tabular}{r|rrrrrrrrr} $x$ & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\ \hline $f(x,1)$ & 3 & 5 & 7 & 6 & 2 & 2 & 1 & 6 & 3 \\ $f(x,2)$ & 7 & 2 & 1 & 2 & 5 & 5 & 3 & 2 & 7 \\ $f(x,4)$ & 3 & 2 & 7 & 2 & 5 & 5 & 1 & 2 & 3 \\ $f(x,8)$ & 7 & 2 & 1 & 2 & 5 & 5 & 3 & 2 & 7 \\ $\cdots$ \\ \end{tabular} \end{center} After this, any value $f(x,k)$ can be calculated by presenting the value $k$ as a sum of powers of two. For example, if we want to calculate the value $f(x,11)$, we first form the representation $11=8+2+1$. Using this, \[f(x,11)=f(f(f(x,8),2),1).\] For example, in the above graph \[f(4,11)=f(f(f(4,8),2),1)=5.\] Such a representation always consists of $O(\log k)$ parts so calculating a value $f(x,k)$ takes $O(\log k)$ time. \section{Cycle detection} \index{cycle} \index{cycle detection} Interesting questions in a successor graph are which node is the first node in the cycle if we begin our walk at node $x$, and what is the size of the cycle. For example, in the graph \begin{center} \begin{tikzpicture}[scale=0.9] \node[draw, circle] (5) at (0,0) {$5$}; \node[draw, circle] (4) at (-2,0) {$4$}; \node[draw, circle] (6) at (-1,1.5) {$6$}; \node[draw, circle] (3) at (-4,0) {$3$}; \node[draw, circle] (2) at (-6,0) {$2$}; \node[draw, circle] (1) at (-8,0) {$1$}; \path[draw,thick,->] (1) -- (2); \path[draw,thick,->] (2) -- (3); \path[draw,thick,->] (3) -- (4); \path[draw,thick,->] (4) -- (5); \path[draw,thick,->] (5) -- (6); \path[draw,thick,->] (6) -- (4); \end{tikzpicture} \end{center} if we begin at node 1, the first node that belongs to the cycle is node 4, and the cycle consists of three nodes (4, 5 and 6). An easy way to detect a cycle is to walk in the graph beginning from node $x$ and keep track of all visited nodes. Once we will visit a node for the second time, the first node in the cycle has been found. This method works in $O(n)$ time and also uses $O(n)$ memory. However, there are better algorithms for cycle detection. The time complexity of those algorithms is still $O(n)$, but they only use $O(1)$ memory. This is an important improvement if $n$ is large. Next we will learn Floyd's algorithm that achieves these properties. \subsubsection{Floyd's algorithm} \index{Floyd's algorithm} \key{Floyd's algorithm} walks forward in the graph using two pointers $a$ and $b$. Both pointers begin at the starting node of the graph. Then, on each turn, pointer $a$ walks one step forward, while pointer $b$ walks two steps forward. The search continues like that until the pointers will meet each other: \begin{lstlisting} a = f(x); b = f(f(x)); while (a != b) { a = f(a); b = f(f(b)); } \end{lstlisting} At this point, pointer $a$ has walked $k$ steps, and pointer $b$ has walked $2k$ steps where the length of the cycle divides $k$. Thus, the first node that belongs to the cycle can be found by moving pointer $a$ to the starting node and advancing the pointers step by step until they will meet again: \begin{lstlisting} a = x; while (a != b) { a = f(a); b = f(b); } \end{lstlisting} Now $a$ and $b$ point to the first node in the cycle that can be reached from node $x$. Finally, the length $c$ of the cycle can be calculated as follows: \begin{lstlisting} b = f(a); c = 1; while (a != b) { b = f(b); c++; } \end{lstlisting} % % \subsubsection{Algoritmi 2 (Brent)} % % \index{Brentin algoritmi@Brentin algoritmi} % % Brentin algoritmi % muodostuu peräkkäisistä vaiheista, joissa osoitin $a$ pysyy % paikallaan ja osoitin $b$ liikkuu $k$ askelta % Alussa $k=1$ ja $k$:n arvo kaksinkertaistuu % joka vaiheen alussa. % Lisäksi $a$ siirretään $b$:n kohdalle vaiheen alussa. % Näin jatketaan, kunnes osoittimet kohtaavat. % % \begin{lstlisting} % a = x; % b = f(x); % c = k = 1; % while (a != b) { % if (c == k) { % a = b; % c = 0; % k *= 2; % } % b = f(b); % c++; % } % \end{lstlisting} % % Nyt tiedossa on, että syklin pituus on $c$. % Tämän jälkeen ensimmäinen sykliin kuuluva solmu löytyy % palauttamalla ensin osoittimet alkuun, % siirtämällä sitten osoitinta $b$ eteenpäin $c$ askelta % ja liikuttamalla lopuksi osoittimia rinnakkain, % kunnes ne osoittavat samaan solmuun. % % \begin{lstlisting} % a = b = x; % for (int i = 0; i < c; i++) b = f(b); % while (a != b) { % a = f(a); % b = f(b); % } % \end{lstlisting} % % Nyt $a$ ja $b$ osoittavat ensimmäiseen sykliin kuuluvaan solmuun. % % Brentin algoritmin etuna Floydin algoritmiin verrattuna on, % että se kutsuu funktiota $f$ harvemmin. % Floydin algoritmi kutsuu funktiota $f$ ensimmäisessä silmukassa % kolmesti joka kierroksella, kun taas Brentin algoritmi % kutsuu funktiota $f$ vain kerran kierrosta kohden.