Open In App

Parenthesis Theorem

Improve
Improve
Like Article
Like
Save
Share
Report

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<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)



Last Updated : 28 Mar, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads