Depth-First Search in Graphs
Depth-First Search in Graphs
Article describes one of the classic methods of searching in the graph - dfs. The
implementation of depth-first search on a disconnected (oriented) graph is presented.
The technique of coloring the vertices and timestamps is described. The classification of
the edges is given. The basic properties of paths and edges are formulated. The
problems associated with DFS are considered.
Imagine that you stay at the entrance to labyrinth. You know that somewhere there
is a way out and it must be found. Labyrinth is a set of tunnels, located deep
underground. You, unfortunately, do not have a flashlight or a torch, but you have a hint
and a lack of fear in the dark.
Described conditions are sufficient to find a way out. It's enough to use the rule of
"right hand": at the entrance of the labyrinth you should take the right hand on the
wall and keep moving so that the right hand slides continuously along the wall. Doing
so, you are sure to find an exit (unless of course there is a path to it from the point of
entry; otherwise you will be taken back to the entrance).
Let's try to complicate the task. Let labyrinth be a collection of linked underground
rooms and tunnels. There is only one entrance to the labyrinth (it is also the exit), and in
each room there is a piece of gold. Your task is to collect all the pieces (exactly all!) and
return to the same place where you entered the labyrinth.
If you do not impose additional conditions or your abilities (skills), the problem in
this formulation is not solvable. In any case, a deterministic algorithm that 100%
guarantees success in collecting all the golden pieces does not exist.
start 4
7
1
3 6
2 8
5
Suppose that in the latest version of the problem in each room there is a lamp that
can be turned on only if you enter this room. Gold Miner (i.e. you) is already armed
with a torch, as well as chalk, that can make any mark on the labyrinth walls. Once you
enter a dark room, you can easily find the switch that turns the lamp on here. We
assume that any two adjacent rooms are connected directly with the short tunnel. So,
being in the one side of the tunnel and looking into it, we can surely determine whether
the light is burning on the other side or not (each tunnel always connects two rooms).
1
5
4
10
11
10
3
7
14
3 6
1
14
3 4
1
2
2 12
13 6 13
2 9 8
7 2 8 8
5 8 7 9
1, 2, 3, 4, 7, 6, 5, 8 1, 2, 3, 4, 5, 6, 8, 7
This method of traversing the labyrinth is called "depth-first search".
Depth-first search (DFS) is one of the methods to traverse a graph G = (V, E), the
essence of which is to go "deep" while it is possible. While depth searching, the vertices
of the graph are numbered, and the edges are marked. The traverse of vertices takes
place according to the principle: if the current vertex has an edge leading to unvisited
vertex, then go there; otherwise return back.
DFS starts with choosing the initial vertex v of the graph G, which is immediately
marked as visited. Then for each unlabeled vertex that is adjacent to v, we recursively
call dfs. When all the vertices that are reachable from v will be marked, the search ends.
If in some (not initial) step of traversing the search stops, but some vertices marked as
unvisited (this possible in the case of oriented or disconnected graph) then we randomly
select one of unvisited vertices and start the search from it. The search process
continues until all the vertices in G will be marked (visited).
Input. The first line contains the number of vertices n (n ≤ 100) and edges m of
the graph. Each of the following m lines contains two vertices a and b – an undirected
edge of the graph. The last line contains the vertex v.
Output. Start a depth-first search from vertex v and print the vertex numbers in the
order of their first visit.
► Let’s build an adjacency matrix from the list of edges. Start a depth-first search
from the specified vertex v. Each time we enter a new vertex, we print its number.
Exercise
Solve the problem for the next graphs:
dfs(1) dfs(1)
1 3 1 6 5
4 2 4
2
5 7 8
3
Algorithm implementation – adjacency matrix
Declare an adjacency matrix g and an array used.
#define MAX 101
int g[MAX][MAX], used[MAX];
Iterate over the vertices i, that can be reached from v. Moving from v to i is
possible if:
• There is an edge (v, i), that is, g[v][i] = 1;
• The vertex i is not visited (used[i] = 0)
If both conditions are true, we continue the depth-first search from vertex i.
The main part of the program. Read the number of vertices n and edges m in the
graph.
scanf("%d %d", &n, &m);
The array g[v] contains a list of vertices adjacent to v. Iterate over the vertices to
that can be reached from v.
for (int to : g[v])
Consider the edge (v, to). We can move to vertex to from v if the vertex to is not
visited (used[to] = 0).
if (used[to] == 0) dfs(to);
}
The main part of the program. Read the number of vertices n and edges m in the
graph.
scanf("%d %d", &n, &m);
Input. The first line contains the number of vertices n (1 ≤ n ≤ 100) and the
number of edges m of the graph. The next m pairs of numbers define the edges. It is
guaranteed that the graph is connected.
Output. Print n – 1 pairs of numbers – the edges that will be included in a tree.
The edges can be displayed in any order.
► Run the depth first search from the first vertex. Build the search tree and print
all its edges.
Example
Graph given in the input, has the form:
1 2
4 3
Depth first serch from vertex 1 will pass through the edges: (1, 2), (2, 3), (3, 4).
1
1
2 3 2
5 4 4 5
6
graph 3 6 DFS tree
Algorithm implementation
The adjacency matrix is stored in array g.
#define MAX 101
int g[MAX][MAX], used[MAX];
Function dfs implements the depth first search from vertex v. Print each edge (v, i)
of the tree during the search.
void dfs(int v)
{
int i;
used[v] = 1;
for(i = 1; i <= n; i++)
if (g[v][i] && !used[i])
{
printf("%d %d\n",v,i);
dfs(i);
}
}
while(m--)
{
scanf("%d %d",&a,&b);
g[a][b] = g[b][a] = 1;
}
Input. The first line contains the number of vertices n (1 ≤ n ≤ 100) in the graph.
Then, an adjacency matrix of size n × n is given, where 1 indicates the presence of an
edge, and 0 indicates its absence. The matrix is symmetric with respect to the main
diagonal.
Example
The graph provided in the example is a tree.
1 2 3
1 1 1
2 4 2 4 2 4
3 5 6 3 5 6 3 5 6
Graph is a tree Graph is NOT a tree Graph is NOT a tree
|V| = 6, |E| = 5 not connected contains a cycle
|V| = 6, |E| = 6
The dfs function performs a depth-first search starting from vertex v. The parent of
vertex v is p. If a cycle is detected, the flag variable is set to 1.
void dfs(int v, int p)
{
If the graph is no longer a tree (flag = 1), there is no need to continue the search.
if (flag) return;
The edge (v, i) will be a back edge and form a cycle if i ≠ p and vertex i is already
visited (used[i] = 1). If a cycle is detected, set flag = 1. If no cycle is detected, continue
the search from vertex i.
for(int i = 0; i < n; i++)
if ((i != p) && g[v][i])
if(used[i]) flag = 1; else dfs(i,v);
}
Count the number of visited vertices during the depth-first search in the variable c.
Set flag = 0 if there is no cycle in the graph. If a cycle is detected, flag becomes 1.
c = 0;
flag = 0;
All vertices are initially unvisited (initialize the array used with zeroes).
memset(used,0,sizeof(used));
Start the depth-first search from vertex 0. Since it is the root of the tree, it has no
parent. Pass the value -1 as the second argument to the dfs function.
dfs(0,-1);
The graph is not a tree if there is a back edge (flag = 1) or if the graph is not
connected (c ≠ n).
if (flag || (c != n)) printf("NO\n"); else printf("YES\n");
All vertices are initially unvisited (initialize the array used with zeroes).
memset(used, 0, sizeof(used));
Read the adjacency matrix g. Count the number of edges in the graph.
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
{
scanf("%d", &g[i][j]);
Edges += g[i][j];
}
Since the graph is undirected, edges (u, v) and (v, u) are considered the same.
Divide the value of Edges by 2.
Edges /= 2;
Count the number of visited vertices during the depth-first search in variable c.
c = 0;
6
2 3 5
The number of connected components res can be found in the next way:
res = 0;
for(i = 1; i <= n; i++)
if (!used[i])
{
dfs(i);
res++;
}
To find the number of connected components for the graph given above, the
following calls will be made:
• dfs(1): visit vertices 1, 2, 3. First connected component, res = 1;
• dfs(4): visit vertices 4, 5. Second connected component, res = 2;
• dfs(6): visit vertices 6. Third connected component, res = 3;
dfs(4)
dfs(1) 1 4
dfs(6)
6
2 3 5
Input. The first line contains the number of vertices n (n ≤ 100) in the graph.
Then, in n lines, an n by n matrix is given – the adjacency matrix of the graph. In the i-
th row, at the j-th position, there is a 1 if vertices i and j are connected by an edge, and 0
if there is no edge between them. The main diagonal of the matrix contains zeroes. The
matrix is symmetric with respect to the main diagonal.
Example
The graph given in a sample is as follows:
1 4
6
2 3 5
#include <stdio.h>
#include <string.h>
#define MAX 102
int n, i, j, res;
int g[MAX][MAX], used[MAX];
void dfs(int v)
{
used[v] = 1;
for(int i = 0; i < n; i++)
if (g[v][i] && !used[i]) dfs(i);
}
int main(void)
{
scanf("%d",&n);
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
scanf("%d",&g[i][j]);
memset(used,0,sizeof(used));
res = 0;
for(i = 0; i < n; i++)
if (!used[i])
{
dfs(i);
res++;
}
printf("%d\n",res);
return 0;
}
E-OLYMP 4000. Depth search Given undirected graph and vertex s. Find the
number of vertices in connected component where vertex s is located.
► Read adjacency matrix. Run DFS from the vertex s. Count the number of
visited vertices during DFS.
E-OLYMP 776. Roads Given non-connected graph. Find minimum edges to add
to get a connected graph.
► Read list of edges. Construct adjacency list because n ≤ 10.000. Use DFS to
count the number of connected components.
Timestamps
In depth first search algorithm you can use the vertex color. Initially all the
vertices are colored white. When the vertex is passed for the first time, it turns gray. The
vertex is processed, if it is fully reviewed all of its adjacent vertices. When the vertex is
processed, it becomes black.
• used[v] = 0, vertex v is not visited (not colored);
• used[v] = 1, vertex v is visited first time, it is gray;
• used[v] = 2, vertex v is processed, it is black;
void dfs(int v)
{
used[v] = 1;
for (int i = 1; i <= n; i++)
if ((g[v][i] == 1) && (used[i] == 0)) dfs(i);
used[v] = 2;
}
2 3 4 2 3 4 2 3 4
5 6 7 5 6 7 5 6 7
1 1 1
2 3 4 2 3 4 2 3 4
5 6 7 5 6 7 5 6 7
Timestamps d[v] and f[v] are integers from 1 to 2|V|. For each vertex v there is
inequality: d[v] < f[v]. Vertex v is white till time d[v], gray from d[v] to f[v] and black
after f[v].
DFS (u)
1. Denote vertex u as passed and color it gray (enter to vertex u);
2. For each vertex v, adjacent to u, and colored to white, call dfs(v);
3. Color vertex u black (exit from vertex u);
7/8
d[v] f[v]
4 4/7 2/15 2 4
v d[v] / f[v] 2
5/14
2/9
5 5/6
7 8
3 9/12
3/4 10/11
1/10 2/9
1 2 1 6 5
5/6
5 3
6/7
3 4 4 2 7
3/8 4/5
2/13 3/12 10/11
Practice. Assign the timestamps for the next graphs:
?/? 3 ?/? 2 ?/?
?/?
1 5
?/? ?/?
?/? 2 8 5 ?/? 3 6
?/?
7 7
?/?
?/?
?/? 4 ?/?
9 1 4
?/? 6 ?/? ?/?
Let t be the global variable, initialized to 0. In the main part run DFS from the
vertex v.
► Read list of edges. Construct adjacency matrix. Add timestamps to dfs function.
Run dfs on disconnected graph.
Practice. Assign the timestamps for the next disconnected graphs:
?/? 3 ?/? 2 ?/?
?/?
1 5
?/? ?/?
?/? 2 8 5 ?/? 3 6
?/?
7 7
?/?
?/?
?/? 4 ?/?
9 1 4
?/? 6 ?/? ?/?
2 7
6 3 8
back edge
4 5 9
If you draw the dfs tree on the plane, back edges usually run from bottom to up: 6
→ 1, 5 → 2, 9 → 7.
Each back edge gives a cycle:
• 6 → 1: cycle 1 – 2 – 6 – 1
• 5 → 2: cycle 2 – 3 – 5 – 2
• 9 → 7: cycle 7 – 8 – 9 – 7
1 5
2 8 5 3 6
7 7
4 9 1 4
6
Consider the picture above. Let we go from vertex 1 to vertex 2. Now we are in the
vetex 2. If we try to move from 2 to 1, we see that vertex 1 is already visited, but the
edge 2 – 1 does not give us a cycle. We can’t move from the vertex to its parent (we can
only return back to the parent if no other way found). So during dfs(v) for each vertex v
we need to hold its parent vertex prev.
E-OLYMP 1390. Car race Given undirected disconnected graph. Check whether
it contains a cycle.
► Read list of edges. Construct adjacency matrix. Set global variable flag = 0
meaning there is no cycle. When cycle will be found, set flag = 1.
void dfs(int v, int prev = -1)
{
// mark vertex v as used
used[v] = 1;
Run depth first search on disconnected graph (try to call DFS from each vertex).
for (i = 1; i <= n; i++)
if (used[i] == 0) dfs(i);
If after these dfs calls flag = 1, cycle exists. Otherwise there is no cycle.
Cycle detection in directed graph
Previous algorithm for cycle detection does not work for oriented graphs. Consider
the next graph:
3 1 2
We start dfs from vertex 1, mark it as used. Then go to vertex 2, mark it as used.
Return back from 2 to 1. Return back from 1. Now we run dfs from 3. Vertices 1 and 2
are already black. Vertex 3 is gray. An edge 3 – 1 runs into the vertex 1 which is already
used.
3 1 2
5/6 1/4 2/3
But an edge 3 – 1 does not create a cycle. It is because this edge runs into black
vertex.
If directed graph contains an edge that runs into GRAY vertex, then it contains a
cycle.
1/
1/ 2/3 1/ 2/3
1 1 2 1 2
2/
4 2
4/ 3 3
3 4 4
4/ 4/
5/
3/
Practice. Assign the timestamps for the next directed graphs. Is there a cycle?
9 ?/? ?/? 2
?/?
?/?
1 ?/?
5
?/?
?/?
2 8 5 ?/? 3 6
?/?
?/? 7 7 ?/?
?/?
4 3 1 4 ?/?
?/? 6 ?/? ?/?
1 2
flag = 0;
for (i = 1; i <= n; i++)
if (used[i] == 0) dfs(i);
E-OLYMP 2270. Find a cycle Given directed graph. Check whether it contains a
cycle. If yes, print any cycle.
► Use adjacency list to run dfs to detect cycles in directed graph. In the stack
(vector) keep the sequence of vertices you passed during dfs to restore found cycle.
These graphs are given at input. Near each vertex of the second graph the state of
the stack is given. It contains a cycle 2 – 4 – 6 – 5.
(1)
1
(1,2) (1,2,3)
(1,2,4,6,5,2)
(1,2,4,6,5) 5 2 3
1 2
(1,2,4,6) 6 4 (1,2,4)
If graph contains a cycle, the statements are not consistent, we should output
"impossible". For example, if for three dwarves we have the next relation among their
heights: Balin < Dori, Dori < Kili, Kili < Balin then this is impossible.
Dori Balin
Kili
Input data contains names of the dwarves. We must assign to each dwarf the vertex
(number). Let’s declare
map<string, int> m;
and keep the number of the vertex for dwarf with the name s in m[s]. Data
structure m is like a database that contains numbers of the vertices for each dwarf. We
read input data and number the dwarves in the same order like they appear first time in
the input.
For example, first appears Dori. We set m[Dori] = 1. Next appears Balin. We set
m[Balin] = 2. In the second condition first appears Balin. m[Balin] is not 0 (it is already
assigned the value). Then goes Kili. We set m[Kili] = 3.
1 2
Dori > Balin m[Dori] = 1 m[Balin] = 2
1 2
+ 3 Dori Balin
Balin > Kili
+ + Kili 3
Dori < Kili m[Kili] = 3
[Link](10001);
Bicolorable graph
Graph is called bicolorable if its vertices can be colored with two colores so that
each edge connects vertices of different colors.
1 2 3
1 2 1 2
4 5 6
4 3 3?
7 8
First two graphs are bicolorable. Third one is not bicolorable, because the color of
the vertex 3 can’t coinside neither with color of vertex 1, nor with color of vertex 2.
You can notice that cycle of even size is bicolorable, but cycle of odd size is not
bicolorable. For example second graph is bicolorable and contains cycles of only even
size.
To check is the graph bicolorable, run dfs from vertex 1. Lets color vertex v with
one of the next ways:
• used[v] = 0, vertex v is not visited, it is not colored;
• used[v] = 1, vertex v is colored with color 1 (for example red);
• used[v] = 2, vertex v is colored with color 2 (for example blue);
Let’s color starting vertex 1 with color 1. Then all its neighbours must be colored
with color 2. All neighbours of vertex with color 2 must be colored with color 1 and so
on. So if vertex v has color col, its neighbours should be colored with color 3 – col.
The problem can appear when we meet the back edge – the edge that runs into
already visited vertex. If v – u is a back edge and their colors are the same, then graph is
not bicolorable.
1
tree edge
2 7
6 3 8
back edge
4 5 9
Everything was fine till we arrive to the vertex 5. Back edge 5 – 2 runs from blue
vertex 5 into blue vertex 2. Graph is not bicolorable. We have a cycle of odd length: 2 –
3 – 5 – 2.
Depth first search starts from vertex v. Vertex v is colored with color. Error is a
global variable, initialised to 0 (initially let graph be bicolorable). If back edge will be
found that connects vertices of the same color, set Error to 1.
void dfs(int v, int color)
{
// if graph is not bicolorable, no sence to continue dfs
if (Error) return;
// color the vertex v
used[v] = color;
Set Error to 0. Run dfs from vertex 1. Color starting vertex 1 with color 1.
Error = 0; dfs(1, 1);
Bipartite graph
Graph G(V, E) is called bipartite (or bigraph) if its vertices V can be divided into
two disjoint sets (V = V1 U V2) so that every edge connects vertices from different sets.
Below given samples of bipartite graphs:
4
1 1 4 4
2 2 3 2 3
6 V1 V2 V1
3 7 1
V1 V2 V2