Given a directed graph represented by the number of vertices V and a list of directed edges, determine whether the graph contains a cycle.
Your task is to implement a function that accepts V (number of vertices) and edges (an array of directed edges where each edge is a pair [u, v]), and returns true if the graph contains at least one cycle, otherwise returns false.
Understanding Back Edges in DFS and Their Role in Cycles
Depth-First Search (DFS) is a powerful technique that can be used to detect cycles in a directed graph. During a DFS traversal, if the graph is connected, it forms a DFS tree (or forest for disconnected graphs).
In such a tree, a cycle exists if and only if there is a back edge. A back edge is an edge that points from a node to one of its ancestors in the DFS traversal this includes self-loops (an edge from a node to itself).
In the left graph, the DFS traversal begins from node 2, and during the traversal, several edges point back to nodes that are already in the current DFS call stack. These edges, marked with red â symbols in the image, are known as back edges, and each one indicates the presence of a cycle in the graph. For example, the edge 3 â 3 is a self-loop, which forms a trivial cycle. The edge 2 â 0 connects to an ancestor in the DFS stack, creating a backward connection and thus a cycle. Similarly, the edge 1 â 2 completes a loop through multiple nodes, further confirming the existence of a cycle.
In the previous post, we have discussed a solution that stores visited vertices in a separate array which stores vertices of the current recursion call stack.
Approach:- Using Coloring - O(V+E) Time and O(V) Space
This algorithm solves the cycle detection problem in a directed graph by using Depth-First Search (DFS) with a coloring technique to track the state of each node during traversal. Nodes are marked as white(unvisited), gray (currently in the recursion stack), or black (fully processed). If during DFS we encounter a gray node, it means weâve found a back edgeâan edge pointing to a node still in the current DFS pathâindicating a cycle. This method efficiently detects cycles by ensuring each node is visited once and each edge is checked once, resulting in a linear time solution relative to the size of the graph (O(V + E)).
Steps:
Convert the edge list into an adjacency list for efficient traversal.
Initialize Color Array,white = 0 , gray = 1, black = 2 to track DFS state of each node.
Start dfs For every unvisited (white) node, start a DFS.
Detect Back Edge in dfs, If you visit a gray node again â cycle exists.
If any dfs finds a cycle, return true; else, return false.
C++
#include<bits/stdc++.h>usingnamespacestd;// Constructs an adjacency list for a directed graphvector<vector<int>>constructadj(intV,constvector<vector<int>>&edges){vector<vector<int>>adj(V);for(constauto&edge:edges){adj[edge[0]].push_back(edge[1]);}returnadj;}// Utility function for DFS traversal and cycle detection// Uses coloring method to detect back edgesbooldfsutil(intu,vector<vector<int>>&adj,vector<int>&color){constintgray=1,black=2;// Mark the current node as being processed (gray)color[u]=gray;// Visit all neighborsfor(intv:adj[u]){// If the neighbor is also gray, we found a back edge -> cycleif(color[v]==gray)returntrue;// If the neighbor is unvisited (white), recur on itif(color[v]==0&&dfsutil(v,adj,color))returntrue;}// After processing all neighbors, mark the node as finished (black)color[u]=black;returnfalse;}// Main function to detect cycle in a directed graphboolisCyclic(intV,constvector<vector<int>>&edges){// Define color values locally:// 0 - white (unvisited), 1 - gray (visiting), 2 - black (visited)vector<int>color(V,0);// Build the adjacency list from edge listvector<vector<int>>adj=constructadj(V,edges);// Perform DFS from every unvisited nodefor(inti=0;i<V;++i){if(color[i]==0){if(dfsutil(i,adj,color))returntrue;}}returnfalse;}// Driver code to test the implementationintmain(){intV=4;vector<vector<int>>edges={{0,1},{0,2},{1,2},{2,0},{2,3},{3,3}};cout<<(isCyclic(V,edges)?"true":"false")<<endl;return0;}
Java
importjava.util.*;publicclassGfG{// Constructs an adjacency list for a directed graphstaticList<Integer>[]constructadj(intV,int[][]edges){List<Integer>[]adj=newArrayList[V];for(inti=0;i<V;i++){adj[i]=newArrayList<>();}for(int[]edge:edges){adj[edge[0]].add(edge[1]);}returnadj;}// Utility function for DFS traversal and cycle// detectionstaticbooleandfsutil(intu,List<Integer>[]adj,int[]color){finalintgray=1,black=2;color[u]=gray;for(intv:adj[u]){if(color[v]==gray)// back edge foundreturntrue;if(color[v]==0&&dfsutil(v,adj,color))// visit unvisitedreturntrue;}color[u]=black;returnfalse;}// Main function to detect cycle in a directed graphstaticbooleanisCyclic(intV,int[][]edges){int[]color=newint[V];List<Integer>[]adj=constructadj(V,edges);for(inti=0;i<V;i++){if(color[i]==0){if(dfsutil(i,adj,color))returntrue;}}returnfalse;}publicstaticvoidmain(String[]args){intV=4;int[][]edges={{0,1},{0,2},{1,2},{2,0},{2,3},{3,3}};System.out.println(isCyclic(V,edges)?"true":"false");}}
Python
# Constructs an adjacency list for a directed graphdefconstructadj(V,edges):adj=[[]for_inrange(V)]foru,vinedges:adj[u].append(v)returnadj# Utility function for DFS traversal and cycle detectiondefdfsutil(u,adj,color):gray=1black=2color[u]=grayforvinadj[u]:ifcolor[v]==gray:# Back edge foundreturnTrueifcolor[v]==0anddfsutil(v,adj,color):# Visit unvisited nodereturnTruecolor[u]=blackreturnFalse# Main function to detect cycle in a directed graphdefisCyclic(V,edges):# 0 - white (unvisited), 1 - gray (visiting), 2 - black (visited)color=[0]*Vadj=constructadj(V,edges)foriinrange(V):ifcolor[i]==0:ifdfsutil(i,adj,color):returnTruereturnFalse# Driver codeV=4edges=[[0,1],[0,2],[1,2],[2,0],[2,3],[3,3]]print("true"ifisCyclic(V,edges)else"false")
C#
usingSystem;usingSystem.Collections.Generic;classGfG{// Constructs an adjacency list for a directed graphstaticList<int>[]constructAdj(intV,int[,]edges){varadj=newList<int>[V];for(inti=0;i<V;i++)adj[i]=newList<int>();intedgeCount=edges.GetLength(0);for(inti=0;i<edgeCount;i++){intfrom=edges[i,0];intto=edges[i,1];adj[from].Add(to);}returnadj;}// Utility function for DFS traversal and cycle// detectionstaticbooldfsutil(intu,List<int>[]adj,int[]color){constintgray=1,black=2;color[u]=gray;foreach(intvinadj[u]){if(color[v]==gray)returntrue;if(color[v]==0&&dfsutil(v,adj,color))returntrue;}color[u]=black;returnfalse;}// Main function to detect cycle in a directed graphstaticboolisCyclic(intV,int[,]edges){int[]color=newint[V];// 0 - white (unvisited), 1 - gray// (visiting), 2 - black (visited)varadj=constructAdj(V,edges);for(inti=0;i<V;i++){if(color[i]==0){if(dfsutil(i,adj,color))returntrue;}}returnfalse;}staticvoidMain(){intV=4;int[,]edges=newint[,]{{0,1},{0,2},{1,2},{2,0},{2,3},{3,3}};Console.WriteLine(isCyclic(V,edges)?"true":"false");}}
JavaScript
// Constructs an adjacency list for a directed graphfunctionconstructAdj(V,edges){constadj=Array.from({length:V},()=>[]);for(const[u,v]ofedges){adj[u].push(v);}returnadj;}// Utility function for DFS traversal and cycle detectionfunctiondfsutil(u,adj,color){constgray=1,black=2;color[u]=gray;// mark as visitingfor(constvofadj[u]){if(color[v]===gray)returntrue;// found a back edge (cycle)if(color[v]===0&&dfsutil(v,adj,color))returntrue;// visit unvisited node}color[u]=black;// mark as visitedreturnfalse;}// Main function to detect cycle in a directed graphfunctionisCyclic(V,edges){constcolor=Array(V).fill(0);// 0 - white (unvisited)constadj=constructAdj(V,edges);for(leti=0;i<V;i++){if(color[i]===0&&dfsutil(i,adj,color)){returntrue;}}returnfalse;}// Driver codeconstV=4;constedges=[[0,1],[0,2],[1,2],[2,0],[2,3],[3,3]];console.log(isCyclic(V,edges)?"true":"false");
Output
true
Time complexity: O(V + E), where V is the number of vertices and E is the number of edges in the graph. Space Complexity: O(V), Since an extra color array is needed of size V.
We do not count the adjacency list in auxiliary space as it is necessary for representing the input graph.