Running DFS on a connected graph generates a DFS spanning tree (or spanning forest if the graph is disconnected). With the help of one more vertex state: EXPLORED  (visited but not yet completed) on top of VISITED (visited and completed), we can use this DFS spanning tree (or forest) to classify graph edges into three types:

  1. Tree edge: The edge traversed by DFS, i.e. an edge from a vertex currently with state:
    EXPLORED to a vertex with state: UNVISITED.
  2. Back edge: Edge that is part of a cycle, i.e. an edge from a vertex currently with state:
    EXPLORED to a vertex with state: EXPLORED too. This is an important application of this algorithm. Note that we usually do not count bi-directional edges as having a ‘cycle’ (We need to remember the parent of each node to distinguish this).
  3. Forward/Cross edges from vertex with state: EXPLORED to vertex with state: VISITED.

Consider the following graph:

In the following picture, and as shown from left to right, we call dfs(0), then dfs(5), and finally dfs(6) on the above graph. We can see that \(1 \rightarrow 2 \rightarrow 3 \rightarrow 1\) is a (true) cycle and we classify edge (\(3 \rightarrow 1\)) as a back edge, whereas \(0 \rightarrow 1 \rightarrow 0\) is not a cycle but it is just a bi-directional edge (\(0-1\)). 

Note that a spanning tree of a connected graph G is a tree that spans (covers) all vertices of G but only using a subset of the edges of G. A disconnected graph G has several connected components. Each component has its own spanning subtree(s). All spanning subtrees of G, one from each component, form what we call a spanning forest.

An adaptation from CP3 of Steven and Felix Halim.


Difficulty level
Video recording
This exercise is mostly suitable for students
#include<stdio.h>
#include<stdlib.h>
typedef struct {
	int V;
	int E;
	int **Adj; // since we need 2 dimensional matrix
} Graph;

Graph* adjacencyMatrix() {
	int i, u, v;
	Graph* G=(Graph*) malloc(sizeof(Graph));
	printf("Enter the number of Vertices: ");
	scanf("%d", &G->V);
	printf("Enter the number of Edges: ");
	scanf("%d", &G->E);
	G->Adj = (int **)malloc(G->V * sizeof(int*));
	for (u = 0; u < G->V; u++)
		G->Adj[u] = (int *)malloc(G->V * sizeof(int));

	for (u = 0; u<G->V; u++)
		for (v = 0; v<G->V; v++)
			G->Adj[u][v] = 0;
	for (i = 0; i < G->E; i++) {
		printf("Enter the edge: ");
		scanf("%d %d", &u,&v);
		// for undirected graph, set both
		G->Adj[u][v] = 1;
		G->Adj[v][u] = 1;
	}
	return G;
}

enum color{EXPLORED,UNVISITED, VISITED}; 

void graphCheck(Graph* G, int u, int Visited[], int Parent[]) {	// DFS for checking graph edge properties
	int j;
 	Visited[u]=EXPLORED;			// color u as EXPLORED instead of VISITED
	for (j = 0; j < G->V; j++) 
	{
		if(G->Adj[u][j]==1)
		{
			if(Visited[j]==UNVISITED) // Tree edge, EXPLORED -> UNVISITED
			{
				Parent[j]=u;
				graphCheck(G, j, Visited, Parent);
			}
			else
				if(Visited[j]==EXPLORED) // 2 options, EXPLORED -> EXPLORED
				{
					if(j==Parent[u])	// to differentiate these two cases
						printf(" Two ways (%d, %d)-(%d, %d)\n", u, j, j, u);
					else		// the most frequent application: check if the graph is cyclic
						printf(" Back Edge (%d, %d) (Cycle)\n", u, j);
				}
				else
					if(Visited[j]==VISITED) //  EXPLORED -> VISITED
						printf(" Forward/Cross Edge (%d, %d)\n", u, j);
		}
	}
	Visited[u]=VISITED;	// after recursion, color u as VISITED (DONE)
}

void DFS(Graph *G) {
	int i, numComp;
	int *Visited, *Parent;
	Visited = (int *)malloc(G->V * sizeof(int));
	Parent = (int *)malloc(G->V * sizeof(int));

    	numComp=0;
	for (i = 0; i < G->V; i++)
	{
		Visited[i] = UNVISITED;
		Parent[i]=0;
	}
	
	// this loop is required if the graph has more than one component
	for (i = 0; i < G ->V; i++)
		if (Visited[i]==UNVISITED)
		{
			printf("Component %d:\n", ++numComp);
			graphCheck(G, i, Visited, Parent);
		}
}


void test() {
	int u, v;
	Graph *G = adjacencyMatrix();
	DFS(G);
}

int main()
{
	test();
	return 0;
}


For the sample graph, the output is like this:
Component 1:
 Two ways (1, 0) - (0, 1)
 Two ways (2, 1) - (1, 2)
 Back Edge (3, 1) (Cycle)
 Two ways (3, 2) - (2, 3)
 Two ways (4, 3) - (3, 4)
 Forward/Cross Edge (1, 3)
Component 2:
Component 3:
 Two ways (7, 6) - (6, 7)
 Two ways (8, 6) - (6, 8)

Back to the list of exercises
Looking for a more challenging exercise, try this one !!
Sorting using the counting sort algorithm