← Back to DSA Course | Chapter 13: Graphs | Lesson 5 of 10

Cycle Detection

Cycle in Graph

A cycle exists when you can start at some vertex, follow edges, and eventually return to that same vertex without repeating an edge — like a circular hallway that eventually leads back to where you started.

Example: Cycle in Graph

#include <iostream>
using namespace std;
int main() {
	int path[] = {0, 1, 2, 0};
	cout << "Path 0->1->2->0 returns to start without repeating an edge: cycle found";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] path = {0, 1, 2, 0};
		System.out.println("Path 0->1->2->0 returns to start without repeating an edge: cycle found");
	}
}
path = [0, 1, 2, 0]
print("Path 0->1->2->0 returns to start without repeating an edge: cycle found")
#include <stdio.h>
int main() {
	int path[] = {0, 1, 2, 0};
	printf("Path 0->1->2->0 returns to start without repeating an edge: cycle found");
	return 0;
}

Visited Vertices

Marking each vertex as visited during traversal lets you spot a cycle the moment you reach a vertex that's already been visited earlier in the current exploration, rather than for the first time.

Example: Visited Vertices

#include <iostream>
using namespace std;
int adj[3][1] = {{1},{2},{0}};
bool visited[3] = {false};
bool dfs(int u) {
	if (visited[u]) return true;
	visited[u] = true;
	return dfs(adj[u][0]);
}
int main() {
	cout << (dfs(0) ? "Cycle found: revisited an already-visited vertex" : "No cycle");
	return 0;
}
public class Main {
	static int[][] adj = {{1},{2},{0}};
	static boolean[] visited = new boolean[3];
	static boolean dfs(int u) {
		if (visited[u]) return true;
		visited[u] = true;
		return dfs(adj[u][0]);
	}
	public static void main(String[] args) {
		System.out.println(dfs(0) ? "Cycle found: revisited an already-visited vertex" : "No cycle");
	}
}
adj = [[1],[2],[0]]
visited = [False]*3
def dfs(u):
    if visited[u]:
        return True
    visited[u] = True
    return dfs(adj[u][0])
print("Cycle found: revisited an already-visited vertex" if dfs(0) else "No cycle")
#include <stdio.h>
int adj[3][1] = {{1},{2},{0}};
int visited[3] = {0};
int dfs(int u) {
	if (visited[u]) return 1;
	visited[u] = 1;
	return dfs(adj[u][0]);
}
int main() {
	printf(dfs(0) ? "Cycle found: revisited an already-visited vertex" : "No cycle");
	return 0;
}

Undirected Graph

In an undirected graph every edge you arrive on immediately makes the vertex you came from look 'already visited,' so DFS must explicitly ignore the edge back to the immediate parent — otherwise every single edge would be flagged as a false cycle.

Example: Undirected Graph

#include <iostream>
using namespace std;
int adj[3][2] = {{1,2},{0,2},{0,1}};
int adjCount[3] = {2,2,2};
bool visited[3] = {false};
bool dfs(int u, int parent) {
	visited[u] = true;
	for (int i = 0; i < adjCount[u]; i++) {
		int v = adj[u][i];
		if (v == parent) continue;
		if (visited[v]) return true;
		if (dfs(v, u)) return true;
	}
	return false;
}
int main() {
	cout << (dfs(0, -1) ? "Cycle found (ignoring the edge back to parent)" : "No cycle");
	return 0;
}
public class Main {
	static int[][] adj = {{1,2},{0,2},{0,1}};
	static boolean[] visited = new boolean[3];
	static boolean dfs(int u, int parent) {
		visited[u] = true;
		for (int v : adj[u]) {
			if (v == parent) continue;
			if (visited[v]) return true;
			if (dfs(v, u)) return true;
		}
		return false;
	}
	public static void main(String[] args) {
		System.out.println(dfs(0, -1) ? "Cycle found (ignoring the edge back to parent)" : "No cycle");
	}
}
adj = [[1,2],[0,2],[0,1]]
visited = [False]*3
def dfs(u, parent):
    visited[u] = True
    for v in adj[u]:
        if v == parent:
            continue
        if visited[v] or dfs(v, u):
            return True
    return False
print("Cycle found (ignoring the edge back to parent)" if dfs(0, -1) else "No cycle")
#include <stdio.h>
int adj[3][2] = {{1,2},{0,2},{0,1}};
int visited[3] = {0};
int dfs(int u, int parent) {
	visited[u] = 1;
	for (int i = 0; i < 2; i++) {
		int v = adj[u][i];
		if (v == parent) continue;
		if (visited[v] || dfs(v, u)) return 1;
	}
	return 0;
}
int main() {
	printf(dfs(0, -1) ? "Cycle found (ignoring the edge back to parent)" : "No cycle");
	return 0;
}

Directed Graph

Directed graphs need a second marker beyond visited: a vertex must be flagged as part of the current recursion path (in-progress), since revisiting a vertex that finished earlier in a different branch is not a cycle, but revisiting one still on the current path is.

Example: Directed Graph

#include <iostream>
using namespace std;
int adj[3][1] = {{1},{2},{0}};
bool visited[3] = {false}, inStack[3] = {false};
bool dfs(int u) {
	visited[u] = true; inStack[u] = true;
	int v = adj[u][0];
	if (inStack[v]) return true;
	if (!visited[v] && dfs(v)) return true;
	inStack[u] = false;
	return false;
}
int main() {
	cout << (dfs(0) ? "Cycle: vertex still in current recursion path" : "No cycle");
	return 0;
}
public class Main {
	static int[][] adj = {{1},{2},{0}};
	static boolean[] visited = new boolean[3], inStack = new boolean[3];
	static boolean dfs(int u) {
		visited[u] = true; inStack[u] = true;
		int v = adj[u][0];
		if (inStack[v]) return true;
		if (!visited[v] && dfs(v)) return true;
		inStack[u] = false;
		return false;
	}
	public static void main(String[] args) {
		System.out.println(dfs(0) ? "Cycle: vertex still in current recursion path" : "No cycle");
	}
}
adj = [[1],[2],[0]]
visited = [False]*3
in_stack = [False]*3
def dfs(u):
    visited[u] = True
    in_stack[u] = True
    v = adj[u][0]
    if in_stack[v]:
        return True
    if not visited[v] and dfs(v):
        return True
    in_stack[u] = False
    return False
print("Cycle: vertex still in current recursion path" if dfs(0) else "No cycle")
#include <stdio.h>
int adj[3][1] = {{1},{2},{0}};
int visited[3] = {0}, inStack[3] = {0};
int dfs(int u) {
	visited[u] = 1; inStack[u] = 1;
	int v = adj[u][0];
	if (inStack[v]) return 1;
	if (!visited[v] && dfs(v)) return 1;
	inStack[u] = 0;
	return 0;
}
int main() {
	printf(dfs(0) ? "Cycle: vertex still in current recursion path" : "No cycle");
	return 0;
}

Why Detect Cycles

Detecting cycles matters for practical reasons: a circular dependency between software packages would cause an infinite build loop, and a cycle in a course-prerequisite graph would make it impossible to ever satisfy every requirement.

Example: Why Detect Cycles

#include <iostream>
using namespace std;
int main() {
	cout << "Package A depends on B depends on A: circular dependency, build loops forever";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		System.out.println("Package A depends on B depends on A: circular dependency, build loops forever");
	}
}
print("Package A depends on B depends on A: circular dependency, build loops forever")
#include <stdio.h>
int main() {
	printf("Package A depends on B depends on A: circular dependency, build loops forever");
	return 0;
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.