Parenthesis Theorem is used in DFS of graph. It states that the descendants in a depth-first-search tree have an interesting property. If v is a descendant of u, then the discovery time of v is later than the discovery time of u.
In any DFS traversal of a graph g = (V, E), for any two vertices u and v exactly one of the following holds:
- The intervals [d[u], f[u]] and [d[v], f[v]] are entirely disjoint and neither u nor v is a descendant of the other in the depth-first forest.
- The interval [d[u], f[u]] is contained within the interval [d[v], f[v]], and u is a descendant of v in a depth-first tree.
- The interval [d[v], f[v]] is contained entirely within the interval [d[u], f[u]], and v is a descendant of u in a depth-first tree.
Classification of Edges:
DFS traversal can be used to classify the edges of input graph G=(V, E). Four edge types can be defined in terms of a depth-first forest:
- Tree Edge: It is an edge that is present in the tree obtained after applying DFS on the graph.
- Forward Edge: It is an edge (u, v) such that v is descendant but not part of the DFS tree.
- Back edge: It is an edge (u, v) such that v is the ancestor of edge u but not part of the DFS tree. The presence of the back edge indicates a cycle in a directed graph.
- Cross Edge: It is an edge that connects two-node such that they do not have any ancestor and a descendant relationship between them.
Given a graph of N vertices and M Edges, the task is to classify the M edges into Tree edges, Forward edges, Backward edges and Cross edges.
Examples:
Input: N = 5, M = 7, arr[][] = { { 1, 2 }, { 1, 3 }, { 3, 4 }, { 1, 4 }, { 2, 5 }, { 5, 1 }, { 3, 2 } } }
Output:
{1, 2} -> Tree Edge
{1, 3} -> Tree Edge
{3, 4} -> Tree Edge
{1, 4} -> Forward Edge
{2, 5} -> Tree Edge
{5, 1} -> Backward Edge
{3, 2} -> Cross Edge
Explanation:
1. Green Edges: Tree Edge
2. Blue Edges: Forward Edge
3. Black Edges: Backward Edge
4. Red Edges: Cross Edge
Below is the given graph for the above information:

Input: N = 5, M = 4, arr[][] = { { 1, 2 }, { 1, 3 }, { 3, 4 }, { 1, 4 } }
Output:
{1, 2} -> Tree Edge
{1, 3} -> Tree Edge
{3, 4} -> Tree Edge
{1, 4} -> Forward Edge
Explanation:
1. Green Edges: Tree Edge
2. Blue Edges: Forward Edge
3. Black Edges: Backward Edge
4. Red Edges: Cross Edge
Below is the given graph for the above information:

Approach:
- Use DFS Traversal on the given graph to find discovery time and finishing time and parent of each node.
- By using Parenthesis Theorem classify the given edges on the below conditions:
- Tree Edge: For any Edge (U, V), if node U is the parent of node V, then (U, V) is the Tree Edge of the given graph.
- Forward Edge: For any Edge (U, V), if discovery time and finishing time of node V fully overlaps with discovery time and finishing time of node U, then (U, V) is the Forward Edge of the given graph.
- Backward Edge: For any Edge (U, V), if discovery time and finishing time of node U fully overlaps with discovery time and finishing time of node V, then (U, V) is the Backward Edge of the given graph.
- Cross Edge: For any Edge (U, V), if discovery time and finishing time of node U doesn't overlaps with discovery time and finishing time of node V, then (U, V) is the Cross Edge of the given graph.
Below is the implementation of the above approach:
C++
// C++ program for the above approach
#include "bits/stdc++.h"
using namespace std;
// For recording time
int tim = 0;
// For creating Graph
vector<list<int> > G;
// For calculating Discovery time
// and finishing time of nodes
vector<int> disc, fin;
// For finding Parent of node
vector<int> Par;
// For storing color of node
vector<char> Color;
// Recursive function for DFS
// to update the
void DFS_Visit(int v)
{
// Make the current nodes as visited
Color[v] = 'G';
// Increment the time
tim = tim + 1;
// Assign the Discovery node of
// node v
disc[v] = tim;
// Traverse the adjacency list of
// vertex v
for (auto& it : G[v]) {
// If the nodes is not visited,
// then mark the parent of the
// current node and call DFS_Visit
// for the current node
if (Color[it] == 'W') {
Par[it] = v;
DFS_Visit(it);
}
}
Color[v] = 'B';
tim = tim + 1;
fin[v] = tim;
}
void DFS(vector<list<int> >& G)
{
// Initialise Par, disc, fin and
// Color vector to size of graph
Par.resize(G.size());
disc.resize(G.size());
fin.resize(G.size());
Color.resize(G.size());
// Initialise the Par[], Color[],
// disc[], fin[]
for (int i = 1; i < G.size(); i++) {
Color[i] = 'W';
Par[i] = 0;
disc[i] = 0;
fin[i] = 0;
}
// For every vertex if nodes is
// not visited then call DFS_Visit
// to update the discovery and
// finishing time of the node
for (int i = 1; i < G.size(); i++) {
// If color is 'W', then
// node is not visited
if (Color[i] == 'W') {
DFS_Visit(i);
}
}
}
// Function to check whether
// time intervals of x and y overlaps
// or not
bool checkOverlap(int x, int y)
{
// Find the time intervals
int x1 = disc[x], y1 = fin[x];
int x2 = disc[y], y2 = fin[y];
// Complete overlaps
if (x2 > x1 && y1 > y2) {
return true;
}
else {
return false;
}
}
// Function to check which Edges
// (x, y) belongs
string checkEdge(int x, int y)
{
// For Tree Edge
// If x is parent of y, then it
// is Tree Edge
if (Par[y] == x) {
return "Tree Edge";
}
// For Forward Edge
else if (checkOverlap(x, y)) {
return "Forward Edge";
}
// For Backward Edge
else if (checkOverlap(y, x)) {
return "Backward Edge";
}
else {
return "Cross Edge";
}
}
// Function call to find the Tree Edge,
// Back Edge, Forward Edge, and Cross Edge
void solve(int arr[][2], int N, int M)
{
// Create graph of N size
G.resize(N + 1);
// Traverse each edges
for (int i = 0; i < M; i++) {
int x = arr[i][0];
int y = arr[i][1];
// Make Directed graph
G[x].push_back(y);
}
// DFS call to calculate discovery
// and finishing time for each node
DFS(G);
// Condition for Tree Edge, Forward
// Edges, Backward Edge and Cross Edge
for (int i = 0; i < M; i++) {
int x = arr[i][0];
int y = arr[i][1];
// Function call to check Edges
cout << "{" << x << ", " << y
<< "} -> " << checkEdge(x, y)
<< endl;
}
}
// Driver Code
int main()
{
// Number of Nodes
int N = 5;
// Number of Edges
int M = 7;
// Edges for the graph
int arr[M][2]
= { { 1, 2 }, { 1, 3 },
{ 3, 4 }, { 1, 4 },
{ 2, 5 }, { 5, 1 },
{ 3, 1 } };
// Function Call
solve(arr, N, M);
return 0;
}
Java
import java.util.*;
public class Main {
// For recording time
static int tim = 0;
// For creating Graph
static ArrayList<Integer>[] G;
// For calculating Discovery time and finishing time of nodes
static int[] disc, fin;
// For finding Parent of node
static int[] Par;
// For storing color of node
static char[] Color;
// Recursive function for DFS to update the
static void DFS_Visit(int v) {
// Make the current nodes as visited
Color[v] = 'G';
// Increment the time
tim += 1;
// Assign the Discovery node of node v
disc[v] = tim;
// Traverse the adjacency list of vertex v
for(int i = 0; i < G[v].size(); i++) {
int it = G[v].get(i);
// If the nodes is not visited, then mark the parent of the current node and call DFS_Visit for the current node
if(Color[it] == 'W') {
Par[it] = v;
DFS_Visit(it);
}
}
Color[v] = 'B';
tim = tim + 1;
fin[v] = tim;
}
static void DFS(ArrayList<Integer>[] graph) {
// Initialise Par, disc, fin and Color vector to size of graph
Par = new int[graph.length];
disc = new int[graph.length];
fin = new int[graph.length];
Color = new char[graph.length];
// Initialise the Par[], Color[], disc[], fin[]
for (int i = 1; i < graph.length; i++) {
Color[i] = 'W';
Par[i] = 0;
disc[i] = 0;
fin[i] = 0;
}
// For every vertex if nodes is not visited then call DFS_Visit to update the discovery and finishing time of the node
for (int i = 1; i < graph.length; i++) {
// If color is 'W', then node is not visited
if (Color[i] == 'W') {
DFS_Visit(i);
}
}
}
// Function to check whether time intervals of x and y overlaps or not
static boolean checkOverlap(int x, int y) {
// Find the time intervals
int x1 = disc[x], y1 = fin[x];
int x2 = disc[y], y2 = fin[y];
// Complete overlaps
if (x2 > x1 && y1 > y2) {
return true;
}
else {
return false;
}
}
// Function to check which Edges (x, y) belongs
static String checkEdge(int x, int y) {
// For Tree Edge If x is parent of y, then it is Tree Edge
if (Par[y] == x) {
return "Tree Edge";
}
// For Forward Edge
else if (checkOverlap(x, y)) {
return "Forward Edge";
}
// For Backward Edge
else if (checkOverlap(y, x)) {
return "Backward Edge";
}
else {
return "Cross Edge";
}
}
// Function call to find the Tree Edge, Back Edge, Forward Edge, and Cross Edge
static void solve(int[][] arr, int N, int M) {
// Create graph of
G = new ArrayList[N + 1];
for (int i = 0; i < N + 1; i++) {
G[i] = new ArrayList<Integer>();
}
// Traverse each edges
for (int i = 0; i < M; i++) {
int x = arr[i][0];
int y = arr[i][1];
// Make Directed graph
G[x].add(y);
}
// DFS call to calculate discovery and finishing time for each node
DFS(G);
// Condition for Tree Edge, Forward Edges, Backward Edge and Cross Edge
for (int i = 0; i < M; i++) {
int x = arr[i][0];
int y = arr[i][1];
// Function call to check Edges
System.out.println("(" + x + "," + y + ")->" + checkEdge(x, y));
}
}
public static void main(String[] args) {
// Number of Nodes
int N = 5;
// Number of Edges
int M = 7;
// Edges for the graph
int[][] arr= {{1, 2} , {1, 3 }, {3, 4} , {1, 4 }, {2, 5} , {5, 1 }, {3, 1}};
// Function Call
solve(arr, N, M);
}
}
Python3
# Python3 program for the above approach For recording time
tim = 0
# For creating Graph
G=[]
# For calculating Discovery time and finishing time of nodes
disc, fin=[],[]
# For finding Parent of node
Par=[]
# For storing color of node
Color=[]
# Recursive function for DFS to update the
def DFS_Visit(v):
global tim
# Make the current nodes as visited
Color[v] = 'G'
# Increment the time
tim += 1
# Assign the Discovery node of node v
disc[v] = tim
# Traverse the adjacency list of vertex v
for it in G[v]:
# If the nodes is not visited, then mark the parent of the current node and call DFS_Visit for the current node
if (Color[it] == 'W') :
Par[it] = v
DFS_Visit(it)
Color[v] = 'B'
tim = tim + 1
fin[v] = tim
def DFS(G):
global Par,disc,fin,Color
# Initialise Par, disc, fin and Color vector to size of graph
Par=[-1]*len(G)
disc=[-1]*len(G)
fin=[-1]*len(G)
Color=['']*len(G)
# Initialise the Par[], Color[], disc[], fin[]
for i in range(1,len(G)):
Color[i] = 'W'
Par[i] = 0
disc[i] = 0
fin[i] = 0
# For every vertex if nodes is not visited then call DFS_Visit to update the discovery and finishing time of the node
for i in range(1,len(G)):
# If color is 'W', then node is not visited
if (Color[i] == 'W') :
DFS_Visit(i)
# Function to check whether time intervals of x and y overlaps or not
def checkOverlap(x, y):
# Find the time intervals
x1 = disc[x]; y1 = fin[x]
x2 = disc[y]; y2 = fin[y]
# Complete overlaps
if (x2 > x1 and y1 > y2) :
return True
else :
return False
# Function to check which Edges (x, y) belongs
def checkEdge(x, y):
# For Tree Edge If x is parent of y, then it is Tree Edge
if (Par[y] == x) :
return "Tree Edge"
# For Forward Edge
elif (checkOverlap(x, y)) :
return "Forward Edge"
# For Backward Edge
elif (checkOverlap(y, x)) :
return "Backward Edge"
else :
return "Cross Edge"
# Function call to find the Tree Edge, Back Edge, Forward Edge, and Cross Edge
def solve(arr, N, M):
global G
# Create graph of N size
G=[[] for _ in range(N + 1)]
# Traverse each edges
for i in range(M):
x = arr[i][0]
y = arr[i][1]
# Make Directed graph
G[x].append(y)
# DFS call to calculate discovery and finishing time for each node
DFS(G)
# Condition for Tree Edge, Forward Edges, Backward Edge and Cross Edge
for i in range(M):
x = arr[i][0]
y = arr[i][1]
# Function call to check Edges
print("({0},{1})->".format(x,y),checkEdge(x, y))
# Driver Code
if __name__ == '__main__':
# Number of Nodes
N = 5
# Number of Edges
M = 7
# Edges for the graph
arr= [[1, 2] , [1, 3 ],
[3, 4] , [1, 4 ],
[2, 5] , [5, 1 ],
[3, 1]]
# Function Call
solve(arr, N, M)
JavaScript
// For recording time
let tim = 0;
// For creating Graph
let G = [];
// For calculating Discovery time
// and finishing time of nodes
let disc = [];
let fin = [];
// For finding Parent of node
let Par = [];
// For storing color of node
let Color = [];
// Recursive function for DFS
// to update the
function DFS_Visit(v) {
// Make the current nodes as visited
Color[v] = "G";
// Increment the time
tim++;
// Assign the Discovery node of
// node v
disc[v] = tim;
// Traverse the adjacency list of
// vertex v
for (let it of G[v]) {
// If the nodes is not visited,
// then mark the parent of the
// current node and call DFS_Visit
// for the current node
if (Color[it] === "W") {
Par[it] = v;
DFS_Visit(it);
}
}
Color[v] = "B";
tim = tim + 1;
fin[v] = tim;
}
function DFS(G) {
// Initialise Par, disc, fin and
// Color vector to size of graph
Par = Array(G.length).fill(-1);
disc = Array(G.length).fill(-1);
fin = Array(G.length).fill(-1);
Color = Array(G.length).fill("");
// Initialise the Par[], Color[],
// disc[], fin[]
for (let i = 1; i < G.length; i++) {
Color[i] = "W";
Par[i] = 0;
disc[i] = 0;
fin[i] = 0;
}
// For every vertex if nodes is
// not visited then call DFS_Visit
// to update the discovery and
// finishing time of the node
for (let i = 1; i < G.length; i++) {
// If color is 'W', then
// node is not visited
if (Color[i] === "W") {
DFS_Visit(i);
}
}
}
// Function to check whether
// time intervals of x and y overlaps
// or not
function checkOverlap(x, y) {
// Find the time intervals
let x1 = disc[x];
let y1 = fin[x];
let x2 = disc[y];
let y2 = fin[y];
// Complete overlaps
if (x2 > x1 && y1 > y2) {
return true;
} else {
return false;
}
}
// Function to check whether
// time intervals of x and y overlaps
// or not
function checkOverlap(x, y) {
// Find the time intervals
const x1 = disc[x];
const y1 = fin[x];
const x2 = disc[y];
const y2 = fin[y];
// Complete overlaps
if (x2 > x1 && y1 > y2) {
return true;
} else {
return false;
}
}
// Function to check which Edges (x, y) belongs
function checkEdge(x, y) {
// For Tree Edge
// If x is parent of y, then it
// is Tree Edge
if (Par[y] === x) {
return "Tree Edge";
}
// For Forward Edge
else if (checkOverlap(x, y)) {
return "Forward Edge";
}
// For Backward Edge
else if (checkOverlap(y, x)) {
return "Backward Edge";
} else {
return "Cross Edge";
}
}
// Function call to find the Tree Edge, Back Edge, Forward Edge, and Cross Edge
function solve(arr, N, M) {
// Create graph of N size
G = Array.from({length: N + 1}, () => []);
// Traverse each edges
for (let i = 0; i < M; i++) {
let x = arr[i][0];
let y = arr[i][1];
// Make Directed graph
G[x].push(y);
}
// DFS call to calculate discovery and finishing time for each node
DFS(G);
// Condition for Tree Edge, Forward Edges, Backward Edge and Cross Edge
for (let i = 0; i < M; i++) {
let x = arr[i][0];
let y = arr[i][1];
// Function call to check Edges
console.log(`(${x},${y})->`, checkEdge(x, y));
}
}
// Driver code
// Number of Nodes
let N = 5;
// Number of Edges
let M = 7;
// Edges for the graph
let arr = [[1, 2], [1, 3], [3, 4], [1, 4], [2, 5], [5, 1], [3, 1]];
// Function call
solve(arr, N, M);
C#
// C# program for the above approach
using System;
using System.Collections.Generic;
public class GFG {
// For recording time
static int tim = 0;
// For creating Graph
static List<int>[] G;
// For calculating Discovery time
// and finishing time of nodes
static int[] disc, fin;
// For finding Parent of node
static int[] Par;
// For storing color of node
static char[] Color;
// Recursive function for DFS
// to update the
static void DFS_Visit(int v) {
// Make the current nodes as visited
Color[v] = 'G';
// Increment the time
tim += 1;
// Assign the Discovery node of
// node v
disc[v] = tim;
// Traverse the adjacency list of
// vertex v
for (int i = 0; i < G[v].Count; i++) {
int it = G[v][i];
// If the nodes is not visited,
// then mark the parent of the
// current node and call DFS_Visit
// for the current node
if (Color[it] == 'W') {
Par[it] = v;
DFS_Visit(it);
}
}
Color[v] = 'B';
tim = tim + 1;
fin[v] = tim;
}
static void DFS(List<int>[] graph) {
// Initialise Par, disc, fin and
// Color vector to size of graph
Par = new int[graph.Length];
disc = new int[graph.Length];
fin = new int[graph.Length];
Color = new char[graph.Length];
// Initialise the Par[], Color[],
// disc[], fin[]
for (int i = 1; i < graph.Length; i++) {
Color[i] = 'W';
Par[i] = 0;
disc[i] = 0;
fin[i] = 0;
}
// For every vertex if nodes is
// not visited then call DFS_Visit
// to update the discovery and
// finishing time of the node
for (int i = 1; i < graph.Length; i++) {
// If color is 'W', then
// node is not visited
if (Color[i] == 'W') {
DFS_Visit(i);
}
}
}
// Function to check whether
// time intervals of x and y overlaps
// or not
static bool checkOverlap(int x, int y) {
int x1 = disc[x], y1 = fin[x];
int x2 = disc[y], y2 = fin[y];
if (x2 > x1 && y1 > y2) {
return true;
}
else {
return false;
}
}
// Function to check which Edges
// (x, y) belongs
static string checkEdge(int x, int y) {
// For Tree Edge
// If x is parent of y, then it
// is Tree Edge
if (Par[y] == x) {
return "Tree Edge";
}
// For Forward Edge
else if (checkOverlap(x, y)) {
return "Forward Edge";
}
// For Backward Edge
else if (checkOverlap(y, x)) {
return "Backward Edge";
}
//// For Cross Edge
else {
return "Cross Edge";
}
}
// Function to check which Edges
// (x, y) belongs
static void solve(int[][] arr, int N, int M) {
// Create graph of N size
G = new List<int>[N + 1];
for (int i = 0; i < N + 1; i++) {
G[i] = new List<int>();
}
for (int i = 0; i < M; i++) {
int x = arr[i][0];
int y = arr[i][1];
G[x].Add(y);
}
// DFS call to calculate discovery
// and finishing time for each node
DFS(G);
for (int i = 0; i < M; i++) {
int x = arr[i][0];
int y = arr[i][1];
Console.WriteLine("(" + x + "," + y + ")->" + checkEdge(x, y));
}
}
//Driver code
public static void Main(string[] args) {
int N = 5;
int M = 7;
int[][] arr = new int[][]{ new int[]{1, 2}, new int[] {1, 3}, new int[] {3, 4}, new int[]{1, 4}, new int[] {2, 5}, new int[] {5, 1}, new int[]{3, 1} };
solve(arr, N, M);
}
}
Output: {1, 2} -> Tree Edge
{1, 3} -> Tree Edge
{3, 4} -> Tree Edge
{1, 4} -> Forward Edge
{2, 5} -> Tree Edge
{5, 1} -> Backward Edge
{3, 1} -> Backward Edge
Time Complexity: O(N), Where N is the total number of nodes in the graph.
Auxiliary Space: O(N)
Explore
DSA Fundamentals
Data Structures
Algorithms
Advanced
Interview Preparation
Practice Problem