Open in App
Not now

# Parenthesis Theorem

• Difficulty Level : Basic
• Last Updated : 14 Mar, 2023

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:

1. Tree Edge: It is an edge that is present in the tree obtained after applying DFS on the graph.
2. Forward Edge: It is an edge (u, v) such that v is descendant but not part of the DFS tree.
3. 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.
4. 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:

1. Use DFS Traversal on the given graph to find discovery time and finishing time and parent of each node.
2. 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 > G;   // For calculating Discovery time // and finishing time of nodes vector disc, fin;   // For finding Parent of node vector Par;   // For storing color of node vector 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 >& 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[] 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[] 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();         }         // 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);

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)

My Personal Notes arrow_drop_up
Related Articles