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++ 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;
} |
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 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)
|
// 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# 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);
} } |
{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)