2017-02-05 10:16:26 +01:00
|
|
|
\chapter{Graph traversal}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-02-05 10:16:26 +01:00
|
|
|
This chapter discusses two fundamental
|
2017-01-07 17:30:14 +01:00
|
|
|
graph algorithms:
|
|
|
|
depth-first search and breadth-first search.
|
|
|
|
Both algorithms are given a starting
|
|
|
|
node in the graph,
|
|
|
|
and they visit all nodes that can be reached
|
|
|
|
from the starting node.
|
|
|
|
The difference in the algorithms is the order
|
|
|
|
in which they visit the nodes.
|
|
|
|
|
|
|
|
\section{Depth-first search}
|
|
|
|
|
|
|
|
\index{depth-first search}
|
|
|
|
|
|
|
|
\key{Depth-first search} (DFS)
|
2017-02-05 10:16:26 +01:00
|
|
|
is a straightforward graph traversal technique.
|
2017-01-07 17:30:14 +01:00
|
|
|
The algorithm begins at a starting node,
|
|
|
|
and proceeds to all other nodes that are
|
|
|
|
reachable from the starting node using
|
2017-05-07 20:18:56 +02:00
|
|
|
the edges of the graph.
|
2017-01-07 17:30:14 +01:00
|
|
|
|
|
|
|
Depth-first search always follows a single
|
|
|
|
path in the graph as long as it finds
|
|
|
|
new nodes.
|
2017-02-05 10:16:26 +01:00
|
|
|
After this, it returns to previous
|
2017-01-07 17:30:14 +01:00
|
|
|
nodes and begins to explore other parts of the graph.
|
|
|
|
The algorithm keeps track of visited nodes,
|
|
|
|
so that it processes each node only once.
|
|
|
|
|
|
|
|
\subsubsection*{Example}
|
|
|
|
|
2017-02-05 10:16:26 +01:00
|
|
|
Let us consider how depth-first search processes
|
2017-01-07 17:30:14 +01:00
|
|
|
the following graph:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle] (1) at (1,5) {$1$};
|
|
|
|
\node[draw, circle] (2) at (3,5) {$2$};
|
|
|
|
\node[draw, circle] (3) at (5,4) {$3$};
|
|
|
|
\node[draw, circle] (4) at (1,3) {$4$};
|
|
|
|
\node[draw, circle] (5) at (3,3) {$5$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (5);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-05-07 20:18:56 +02:00
|
|
|
We may begin the search at any node of the graph;
|
|
|
|
now we will begin the search at node 1.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-07 17:30:14 +01:00
|
|
|
The search first proceeds to node 2:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
|
|
|
\node[draw, circle,fill=lightgray] (2) at (3,5) {$2$};
|
|
|
|
\node[draw, circle] (3) at (5,4) {$3$};
|
|
|
|
\node[draw, circle] (4) at (1,3) {$4$};
|
|
|
|
\node[draw, circle] (5) at (3,3) {$5$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (5);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (2);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-07 17:30:14 +01:00
|
|
|
After this, nodes 3 and 5 will be visited:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
|
|
|
\node[draw, circle,fill=lightgray] (2) at (3,5) {$2$};
|
|
|
|
\node[draw, circle,fill=lightgray] (3) at (5,4) {$3$};
|
|
|
|
\node[draw, circle] (4) at (1,3) {$4$};
|
|
|
|
\node[draw, circle,fill=lightgray] (5) at (3,3) {$5$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (5);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
|
|
|
|
\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);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-07 17:30:14 +01:00
|
|
|
The neighbors of node 5 are 2 and 3,
|
|
|
|
but the search has already visited both of them,
|
2017-05-07 20:18:56 +02:00
|
|
|
so it is time to return to the previous nodes.
|
2017-01-07 17:30:14 +01:00
|
|
|
Also the neighbors of nodes 3 and 2
|
2017-02-05 10:16:26 +01:00
|
|
|
have been visited, so we next move
|
2017-01-07 17:30:14 +01:00
|
|
|
from node 1 to node 4:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
|
|
|
\node[draw, circle,fill=lightgray] (2) at (3,5) {$2$};
|
|
|
|
\node[draw, circle,fill=lightgray] (3) at (5,4) {$3$};
|
|
|
|
\node[draw, circle,fill=lightgray] (4) at (1,3) {$4$};
|
|
|
|
\node[draw, circle,fill=lightgray] (5) at (3,3) {$5$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (5);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (4);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-07 17:30:14 +01:00
|
|
|
After this, the search terminates because it has visited
|
|
|
|
all nodes.
|
|
|
|
|
|
|
|
The time complexity of depth-first search is $O(n+m)$
|
|
|
|
where $n$ is the number of nodes and $m$ is the
|
|
|
|
number of edges,
|
|
|
|
because the algorithm processes each node and edge once.
|
|
|
|
|
|
|
|
\subsubsection*{Implementation}
|
|
|
|
|
|
|
|
Depth-first search can be conveniently
|
|
|
|
implemented using recursion.
|
|
|
|
The following function \texttt{dfs} begins
|
|
|
|
a depth-first search at a given node.
|
|
|
|
The function assumes that the graph is
|
2017-02-05 10:16:26 +01:00
|
|
|
stored as adjacency lists in an array
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{lstlisting}
|
2017-04-17 14:27:43 +02:00
|
|
|
vector<int> adj[N];
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{lstlisting}
|
2017-01-07 17:30:14 +01:00
|
|
|
and also maintains an array
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{lstlisting}
|
2017-05-28 11:07:31 +02:00
|
|
|
bool visited[N];
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{lstlisting}
|
2017-01-07 17:30:14 +01:00
|
|
|
that keeps track of the visited nodes.
|
2017-04-17 12:58:04 +02:00
|
|
|
Initially, each array value is \texttt{false},
|
2017-01-07 17:30:14 +01:00
|
|
|
and when the search arrives at node $s$,
|
2017-05-28 11:07:31 +02:00
|
|
|
the value of \texttt{visited}[$s$] becomes \texttt{true}.
|
2017-01-07 17:30:14 +01:00
|
|
|
The function can be implemented as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{lstlisting}
|
2017-01-07 17:30:14 +01:00
|
|
|
void dfs(int s) {
|
2017-05-28 11:07:31 +02:00
|
|
|
if (visited[s]) return;
|
|
|
|
visited[s] = true;
|
2017-01-07 17:30:14 +01:00
|
|
|
// process node s
|
2017-04-17 14:27:43 +02:00
|
|
|
for (auto u: adj[s]) {
|
2017-01-07 17:30:14 +01:00
|
|
|
dfs(u);
|
2016-12-28 23:54:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2017-01-07 17:30:14 +01:00
|
|
|
\section{Breadth-first search}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-07 17:30:14 +01:00
|
|
|
\index{breadth-first search}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-07 17:30:14 +01:00
|
|
|
\key{Breadth-first search} (BFS) visits the nodes
|
|
|
|
in increasing order of their distance
|
|
|
|
from the starting node.
|
|
|
|
Thus, we can calculate the distance
|
|
|
|
from the starting node to all other
|
|
|
|
nodes using breadth-first search.
|
|
|
|
However, breadth-first search is more difficult
|
|
|
|
to implement than depth-first search.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-07 17:30:14 +01:00
|
|
|
Breadth-first search goes through the nodes
|
|
|
|
one level after another.
|
|
|
|
First the search explores the nodes whose
|
|
|
|
distance from the starting node is 1,
|
|
|
|
then the nodes whose distance is 2, and so on.
|
|
|
|
This process continues until all nodes
|
|
|
|
have been visited.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-07 17:30:14 +01:00
|
|
|
\subsubsection*{Example}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-07 20:18:56 +02:00
|
|
|
Let us consider how breadth-first search processes
|
2017-01-07 17:30:14 +01:00
|
|
|
the following graph:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\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,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (6);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\path[draw,thick,-] (5) -- (6);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-05-07 20:18:56 +02:00
|
|
|
Suppose that the search begins at node 1.
|
2017-01-07 17:30:14 +01:00
|
|
|
First, we process all nodes that can be reached
|
|
|
|
from node 1 using a single edge:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
|
|
|
\node[draw, circle,fill=lightgray] (2) at (3,5) {$2$};
|
|
|
|
\node[draw, circle] (3) at (5,5) {$3$};
|
|
|
|
\node[draw, circle,fill=lightgray] (4) at (1,3) {$4$};
|
|
|
|
\node[draw, circle] (5) at (3,3) {$5$};
|
|
|
|
\node[draw, circle] (6) at (5,3) {$6$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (6);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\path[draw,thick,-] (5) -- (6);
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (2);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (4);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-02-05 10:16:26 +01:00
|
|
|
After this, we proceed to nodes 3 and 5:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
|
|
|
\node[draw, circle,fill=lightgray] (2) at (3,5) {$2$};
|
|
|
|
\node[draw, circle,fill=lightgray] (3) at (5,5) {$3$};
|
|
|
|
\node[draw, circle,fill=lightgray] (4) at (1,3) {$4$};
|
|
|
|
\node[draw, circle,fill=lightgray] (5) at (3,3) {$5$};
|
|
|
|
\node[draw, circle] (6) at (5,3) {$6$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (6);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\path[draw,thick,-] (5) -- (6);
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (2) -- (3);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (2) -- (5);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-07 17:30:14 +01:00
|
|
|
Finally, we visit node 6:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
|
|
|
\node[draw, circle,fill=lightgray] (2) at (3,5) {$2$};
|
|
|
|
\node[draw, circle,fill=lightgray] (3) at (5,5) {$3$};
|
|
|
|
\node[draw, circle,fill=lightgray] (4) at (1,3) {$4$};
|
|
|
|
\node[draw, circle,fill=lightgray] (5) at (3,3) {$5$};
|
|
|
|
\node[draw, circle,fill=lightgray] (6) at (5,3) {$6$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (6);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\path[draw,thick,-] (5) -- (6);
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (3) -- (6);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (5) -- (6);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-07 17:30:14 +01:00
|
|
|
Now we have calculated the distances
|
2017-05-07 20:18:56 +02:00
|
|
|
from the starting node to all nodes of the graph.
|
2017-01-07 17:30:14 +01:00
|
|
|
The distances are as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{tabular}{ll}
|
|
|
|
\\
|
2017-01-07 17:30:14 +01:00
|
|
|
node & distance \\
|
2016-12-28 23:54:51 +01:00
|
|
|
\hline
|
|
|
|
1 & 0 \\
|
|
|
|
2 & 1 \\
|
|
|
|
3 & 2 \\
|
|
|
|
4 & 1 \\
|
|
|
|
5 & 2 \\
|
|
|
|
6 & 3 \\
|
|
|
|
\\
|
|
|
|
\end{tabular}
|
|
|
|
|
2017-01-07 17:30:14 +01:00
|
|
|
Like in depth-first search,
|
|
|
|
the time complexity of breadth-first search
|
2017-05-07 20:18:56 +02:00
|
|
|
is $O(n+m)$, where $n$ is the number of nodes
|
2017-01-07 17:30:14 +01:00
|
|
|
and $m$ is the number of edges.
|
|
|
|
|
|
|
|
\subsubsection*{Implementation}
|
|
|
|
|
|
|
|
Breadth-first search is more difficult
|
2017-02-05 10:16:26 +01:00
|
|
|
to implement than depth-first search,
|
2017-01-07 17:30:14 +01:00
|
|
|
because the algorithm visits nodes
|
2017-02-05 10:16:26 +01:00
|
|
|
in different parts of the graph.
|
|
|
|
A typical implementation is based on
|
2017-02-17 21:13:30 +01:00
|
|
|
a queue that contains nodes.
|
2017-01-07 17:30:14 +01:00
|
|
|
At each step, the next node in the queue
|
|
|
|
will be processed.
|
|
|
|
|
2017-04-17 12:58:04 +02:00
|
|
|
The following code assumes that the graph is stored
|
|
|
|
as adjacency lists and maintains the following
|
|
|
|
data structures:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{lstlisting}
|
|
|
|
queue<int> q;
|
2017-05-28 11:07:31 +02:00
|
|
|
bool visited[N];
|
|
|
|
int distance[N];
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{lstlisting}
|
2017-04-17 12:58:04 +02:00
|
|
|
|
|
|
|
The queue \texttt{q}
|
2017-05-07 20:18:56 +02:00
|
|
|
contains nodes to be processed
|
|
|
|
in increasing order of their distance.
|
2017-01-07 17:30:14 +01:00
|
|
|
New nodes are always added to the end
|
|
|
|
of the queue, and the node at the beginning
|
|
|
|
of the queue is the next node to be processed.
|
2017-05-28 11:07:31 +02:00
|
|
|
The array \texttt{visited} indicates
|
2017-04-17 12:58:04 +02:00
|
|
|
which nodes the search has already visited,
|
2017-05-28 11:07:31 +02:00
|
|
|
and the array \texttt{distance} will contain the
|
2017-05-07 20:18:56 +02:00
|
|
|
distances from the starting node to all nodes of the graph.
|
2017-04-17 12:58:04 +02:00
|
|
|
|
|
|
|
The search can be implemented as follows,
|
|
|
|
starting at node $x$:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{lstlisting}
|
2017-05-28 11:07:31 +02:00
|
|
|
visited[x] = true;
|
|
|
|
distance[x] = 0;
|
2016-12-28 23:54:51 +01:00
|
|
|
q.push(x);
|
|
|
|
while (!q.empty()) {
|
|
|
|
int s = q.front(); q.pop();
|
2017-01-07 17:30:14 +01:00
|
|
|
// process node s
|
2017-04-17 14:27:43 +02:00
|
|
|
for (auto u : adj[s]) {
|
2017-05-28 11:07:31 +02:00
|
|
|
if (visited[u]) continue;
|
|
|
|
visited[u] = true;
|
|
|
|
distance[u] = distance[s]+1;
|
2016-12-28 23:54:51 +01:00
|
|
|
q.push(u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2017-01-07 18:17:37 +01:00
|
|
|
\section{Applications}
|
|
|
|
|
2017-02-05 10:16:26 +01:00
|
|
|
Using the graph traversal algorithms,
|
2017-05-07 20:18:56 +02:00
|
|
|
we can check many properties of graphs.
|
|
|
|
Usually, both depth-first search and
|
2017-12-10 11:06:32 +01:00
|
|
|
breadth-first search may be used,
|
2017-01-07 18:17:37 +01:00
|
|
|
but in practice, depth-first search
|
2017-02-05 10:16:26 +01:00
|
|
|
is a better choice, because it is
|
2017-01-07 18:17:37 +01:00
|
|
|
easier to implement.
|
|
|
|
In the following applications we will
|
|
|
|
assume that the graph is undirected.
|
|
|
|
|
|
|
|
\subsubsection{Connectivity check}
|
|
|
|
|
|
|
|
\index{connected graph}
|
|
|
|
|
|
|
|
A graph is connected if there is a path
|
2017-05-07 20:18:56 +02:00
|
|
|
between any two nodes of the graph.
|
2017-01-07 18:17:37 +01:00
|
|
|
Thus, we can check if a graph is connected
|
2017-05-07 20:18:56 +02:00
|
|
|
by starting at an arbitrary node and
|
2017-01-07 18:17:37 +01:00
|
|
|
finding out if we can reach all other nodes.
|
|
|
|
|
|
|
|
For example, in the graph
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle] (2) at (7,5) {$2$};
|
|
|
|
\node[draw, circle] (1) at (3,5) {$1$};
|
|
|
|
\node[draw, circle] (3) at (5,4) {$3$};
|
|
|
|
\node[draw, circle] (5) at (7,3) {$5$};
|
|
|
|
\node[draw, circle] (4) at (3,3) {$4$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (4);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-07 18:17:37 +01:00
|
|
|
a depth-first search from node $1$ visits
|
|
|
|
the following nodes:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle] (2) at (7,5) {$2$};
|
|
|
|
\node[draw, circle,fill=lightgray] (1) at (3,5) {$1$};
|
|
|
|
\node[draw, circle,fill=lightgray] (3) at (5,4) {$3$};
|
|
|
|
\node[draw, circle] (5) at (7,3) {$5$};
|
|
|
|
\node[draw, circle,fill=lightgray] (4) at (3,3) {$4$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (4);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (3);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (3) -- (4);
|
|
|
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-02-05 10:16:26 +01:00
|
|
|
Since the search did not visit all the nodes,
|
2017-01-07 18:17:37 +01:00
|
|
|
we can conclude that the graph is not connected.
|
2017-02-05 10:16:26 +01:00
|
|
|
In a similar way, we can also find all connected components
|
2017-02-17 21:13:30 +01:00
|
|
|
of a graph by iterating through the nodes and always
|
2017-02-05 10:16:26 +01:00
|
|
|
starting a new depth-first search if the current node
|
|
|
|
does not belong to any component yet.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-07 18:17:37 +01:00
|
|
|
\subsubsection{Finding cycles}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-07 18:17:37 +01:00
|
|
|
\index{cycle}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-02-05 10:16:26 +01:00
|
|
|
A graph contains a cycle if during a graph traversal,
|
2017-01-07 18:17:37 +01:00
|
|
|
we find a node whose neighbor (other than the
|
|
|
|
previous node in the current path) has already been
|
|
|
|
visited.
|
|
|
|
For example, the graph
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
2017-02-17 21:13:30 +01:00
|
|
|
\node[draw, circle] (2) at (7,5) {$2$};
|
|
|
|
\node[draw, circle] (1) at (3,5) {$1$};
|
|
|
|
\node[draw, circle] (3) at (5,4) {$3$};
|
|
|
|
\node[draw, circle] (5) at (7,3) {$5$};
|
|
|
|
\node[draw, circle] (4) at (3,3) {$4$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (4);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (3) -- (5);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
contains two cycles and we can find one
|
|
|
|
of them as follows:
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
2016-12-28 23:54:51 +01:00
|
|
|
\node[draw, circle,fill=lightgray] (2) at (7,5) {$2$};
|
|
|
|
\node[draw, circle,fill=lightgray] (1) at (3,5) {$1$};
|
|
|
|
\node[draw, circle,fill=lightgray] (3) at (5,4) {$3$};
|
|
|
|
\node[draw, circle,fill=lightgray] (5) at (7,3) {$5$};
|
|
|
|
\node[draw, circle] (4) at (3,3) {$4$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (3);
|
|
|
|
\path[draw,thick,-] (1) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (4);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (3) -- (5);
|
|
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) -- (3);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (3) -- (2);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (2) -- (5);
|
|
|
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-05-07 20:18:56 +02:00
|
|
|
After moving from node 2 to node 5 we notice that
|
|
|
|
the neighbor 3 of node 5 has already been visited.
|
2017-01-07 18:17:37 +01:00
|
|
|
Thus, the graph contains a cycle that goes through node 3,
|
|
|
|
for example, $3 \rightarrow 2 \rightarrow 5 \rightarrow 3$.
|
|
|
|
|
|
|
|
Another way to find out whether a graph contains a cycle
|
|
|
|
is to simply calculate the number of nodes and edges
|
|
|
|
in every component.
|
|
|
|
If a component contains $c$ nodes and no cycle,
|
2017-02-17 21:13:30 +01:00
|
|
|
it must contain exactly $c-1$ edges
|
|
|
|
(so it has to be a tree).
|
2017-01-07 18:17:37 +01:00
|
|
|
If there are $c$ or more edges, the component
|
2017-02-05 10:16:26 +01:00
|
|
|
surely contains a cycle.
|
2017-01-07 18:17:37 +01:00
|
|
|
|
|
|
|
\subsubsection{Bipartiteness check}
|
|
|
|
|
|
|
|
\index{bipartite graph}
|
|
|
|
|
|
|
|
A graph is bipartite if its nodes can be colored
|
|
|
|
using two colors so that there are no adjacent
|
2017-02-05 10:16:26 +01:00
|
|
|
nodes with the same color.
|
2017-02-12 19:33:00 +01:00
|
|
|
It is surprisingly easy to check if a graph
|
2017-02-05 10:16:26 +01:00
|
|
|
is bipartite using graph traversal algorithms.
|
2017-01-07 18:17:37 +01:00
|
|
|
|
|
|
|
The idea is to color the starting node blue,
|
|
|
|
all its neighbors red, all their neighbors blue, and so on.
|
|
|
|
If at some point of the search we notice that
|
|
|
|
two adjacent nodes have the same color,
|
|
|
|
this means that the graph is not bipartite.
|
|
|
|
Otherwise the graph is bipartite and one coloring
|
|
|
|
has been found.
|
|
|
|
|
|
|
|
For example, the graph
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle] (2) at (5,5) {$2$};
|
|
|
|
\node[draw, circle] (1) at (3,5) {$1$};
|
|
|
|
\node[draw, circle] (3) at (7,4) {$3$};
|
|
|
|
\node[draw, circle] (5) at (5,3) {$5$};
|
|
|
|
\node[draw, circle] (4) at (3,3) {$4$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\path[draw,thick,-] (5) -- (4);
|
|
|
|
\path[draw,thick,-] (4) -- (1);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (5) -- (3);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-02-17 21:13:30 +01:00
|
|
|
is not bipartite, because a search from node 1
|
2017-02-05 10:16:26 +01:00
|
|
|
proceeds as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}
|
|
|
|
\node[draw, circle,fill=red!40] (2) at (5,5) {$2$};
|
|
|
|
\node[draw, circle,fill=blue!40] (1) at (3,5) {$1$};
|
|
|
|
\node[draw, circle,fill=blue!40] (3) at (7,4) {$3$};
|
|
|
|
\node[draw, circle,fill=red!40] (5) at (5,3) {$5$};
|
|
|
|
\node[draw, circle] (4) at (3,3) {$4$};
|
|
|
|
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (2) -- (5);
|
|
|
|
\path[draw,thick,-] (5) -- (4);
|
|
|
|
\path[draw,thick,-] (4) -- (1);
|
|
|
|
\path[draw,thick,-] (2) -- (3);
|
|
|
|
\path[draw,thick,-] (5) -- (3);
|
|
|
|
|
|
|
|
\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-17 21:13:30 +01:00
|
|
|
We notice that the color or both nodes 2 and 5
|
2017-01-07 18:17:37 +01:00
|
|
|
is red, while they are adjacent nodes in the graph.
|
|
|
|
Thus, the graph is not bipartite.
|
|
|
|
|
2017-02-05 10:16:26 +01:00
|
|
|
This algorithm always works, because when there
|
2017-01-07 18:17:37 +01:00
|
|
|
are only two colors available,
|
|
|
|
the color of the starting node in a component
|
|
|
|
determines the colors of all other nodes in the component.
|
2017-02-05 10:16:26 +01:00
|
|
|
It does not make any difference whether the
|
2017-01-07 18:17:37 +01:00
|
|
|
starting node is red or blue.
|
|
|
|
|
|
|
|
Note that in the general case,
|
|
|
|
it is difficult to find out if the nodes
|
|
|
|
in a graph can be colored using $k$ colors
|
|
|
|
so that no adjacent nodes have the same color.
|
|
|
|
Even when $k=3$, no efficient algorithm is known
|
2017-03-10 13:17:04 +01:00
|
|
|
but the problem is NP-hard.
|