Given a directed graph, the task is to identify tree, forward, back and cross edges present in the graph.
Note: There can be multiple answers.
Example:
Input: Graph
Output:
Tree Edges: 1->2, 2->4, 4->6, 1->3, 3->5, 5->7, 5->8
Forward Edges: 1->8
Back Edges: 6->2
Cross Edges: 5->4
- Tree Edge: It is an edge which is present in the tree obtained after applying DFS on the graph. All the Green edges are tree edges.
- Forward Edge: It is an edge (u, v) such that v is a descendant but not part of the DFS tree. An edge from 1 to 8 is a forward edge.
- Back edge: It is an edge (u, v) such that v is the ancestor of node u but is not part of the DFS tree. Edge from 6 to 2 is a back edge. Presence of back edge indicates a cycle in directed graph.
- Cross Edge: It is an edge that connects two nodes such that they do not have any ancestor and a descendant relationship between them. The edge from node 5 to 4 is a cross edge.
Approach:
The idea is to perform a Depth-First Search (DFS) traversal of the directed graph while tracking discovery and finish times to classify edges into Tree, Forward, Back, and Cross edges based on their relationship with visited nodes and the DFS call stack.
Step by step approach:
- Initialize
discoveryandfinishtime arrays, avisitedarray, and aninStackarray to track nodes in the current DFS recursion stack. - Start DFS from each unvisited node, updating discovery times and marking nodes as visited and in the stack.
- If an adjacent node
vis unvisited, classify the edge(u, v)as a Tree Edge and recursively call DFS onv. - If
vis already visited and present in the recursion stack, classify(u, v)as a Back Edge, indicating a cycle. - If
vis already visited but not in the recursion stack anddiscovery[u] < discovery[v], classify(u, v)as a Forward Edge, meaningvis a descendant ofu. - If
vis already visited, not in the stack, anddiscovery[u] > discovery[v], classify(u, v)as a Cross Edge, indicating a connection between different DFS trees. - After processing all neighbors of
u, removeufrom the stack and update its finish time.
Below is the implementation of the above approach:
// c++ program to identify Tree, Back,
// Edge and Cross Edges in DFS of Graph
#include <bits/stdc++.h>
using namespace std;
void dfs(int u, vector<vector<int>>& adj, vector<bool>& visited,
vector<bool>& inStack, vector<int>& discovery,
vector<int>& finish, int& timeStamp,
vector<vector<vector<int>>>& edges) {
visited[u] = true;
inStack[u] = true;
discovery[u] = ++timeStamp;
for(int v : adj[u]) {
if(!visited[v]) {
// Tree Edge
edges[0].push_back({u, v});
dfs(v, adj, visited, inStack,
discovery, finish, timeStamp, edges);
}
else {
if(inStack[v]) {
// Back Edge
edges[2].push_back({u, v});
}
else if(discovery[u] < discovery[v]) {
// Forward Edge
edges[1].push_back({u, v});
}
else {
// Cross Edge
edges[3].push_back({u, v});
}
}
}
inStack[u] = false;
finish[u] = ++timeStamp;
}
vector<vector<vector<int>>> classifyEdges(vector<vector<int>>& adj) {
int n = adj.size();
// Array to store discovery timeStamp of node
vector<int> discovery(n, 0);
// Array to store finish timeStamp of node.
vector<int> finish(n, 0);
// Array to check if node is visited
vector<bool> visited(n, false);
// Array to check if node is in call stack.
vector<bool> inStack(n, false);
int timeStamp = 0;
// Initialize result vector with
// 4 empty vectors for each edge type
vector<vector<vector<int>>> edges(4);
// Run DFS for each unvisited vertex
for(int i = 0; i < n; i++) {
if(!visited[i]) {
dfs(i, adj, visited, inStack,
discovery, finish, timeStamp, edges);
}
}
return edges;
}
int main() {
vector<vector<int>> adj = {
{1, 2, 7},
{3},
{4},
{5},
{3, 6, 7},
{1},
{},
{}
};
vector<vector<vector<int>>> edges = classifyEdges(adj);
vector<string> edgeNames = {"Tree Edges", "Forward Edges",
"Back Edges", "Cross Edges"};
for(int i = 0; i < 4; i++) {
cout << edgeNames[i] << ": ";
for(auto& edge : edges[i]) {
cout << edge[0] << "->" << edge[1] << " ";
}
cout << endl;
}
return 0;
}
// Java program to identify Tree, Back,
// Edge and Cross Edges in DFS of Graph
import java.util.*;
class GfG {
static void dfs(int u, int[][] adj, boolean[] visited,
boolean[] inStack, int[] discovery, int[] finish,
int[] timeStamp, List<int[]>[] edges) {
visited[u] = true;
inStack[u] = true;
discovery[u] = ++timeStamp[0];
for (int v : adj[u]) {
if (!visited[v]) {
// Tree Edge
edges[0].add(new int[]{u, v});
dfs(v, adj, visited, inStack,
discovery, finish, timeStamp, edges);
} else {
if (inStack[v]) {
// Back Edge
edges[2].add(new int[]{u, v});
} else if (discovery[u] < discovery[v]) {
// Forward Edge
edges[1].add(new int[]{u, v});
} else {
// Cross Edge
edges[3].add(new int[]{u, v});
}
}
}
inStack[u] = false;
finish[u] = ++timeStamp[0];
}
static List<int[]>[] classifyEdges(int[][] adj) {
int n = adj.length;
int[] discovery = new int[n];
int[] finish = new int[n];
boolean[] visited = new boolean[n];
boolean[] inStack = new boolean[n];
int[] timeStamp = {0};
List<int[]>[] edges = new List[4];
for (int i = 0; i < 4; i++) {
edges[i] = new ArrayList<>();
}
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(i, adj, visited, inStack, discovery, finish, timeStamp, edges);
}
}
return edges;
}
public static void main(String[] args) {
int[][] adj = {
{1, 2, 7},
{3},
{4},
{5},
{3, 6, 7},
{1},
{},
{}
};
List<int[]>[] edges = classifyEdges(adj);
String[] edgeNames = {"Tree Edges", "Forward Edges",
"Back Edges", "Cross Edges"};
for (int i = 0; i < 4; i++) {
System.out.print(edgeNames[i] + ": ");
for (int[] edge : edges[i]) {
System.out.print(edge[0] + "->" + edge[1] + " ");
}
System.out.println();
}
}
}
# Python program to identify Tree, Back,
# Edge and Cross Edges in DFS of Graph
# Function to perform DFS
def dfs(u, adj, visited, inStack, discovery, finish, timeStamp, edges):
visited[u] = True
inStack[u] = True
discovery[u] = timeStamp[0] = timeStamp[0] + 1
for v in adj[u]:
if not visited[v]:
# Tree Edge
edges[0].append([u, v])
dfs(v, adj, visited, inStack, discovery, finish, timeStamp, edges)
else:
if inStack[v]:
# Back Edge
edges[2].append([u, v])
elif discovery[u] < discovery[v]:
# Forward Edge
edges[1].append([u, v])
else:
# Cross Edge
edges[3].append([u, v])
inStack[u] = False
finish[u] = timeStamp[0] = timeStamp[0] + 1
# Function to classify edges
def classifyEdges(adj):
n = len(adj)
discovery = [0] * n
finish = [0] * n
visited = [False] * n
inStack = [False] * n
timeStamp = [0]
edges = [[] for _ in range(4)]
for i in range(n):
if not visited[i]:
dfs(i, adj, visited, inStack, discovery, finish, timeStamp, edges)
return edges
if __name__ == "__main__":
adj = [
[1, 2, 7],
[3],
[4],
[5],
[3, 6, 7],
[1],
[],
[]
]
edges = classifyEdges(adj)
edgeNames = ["Tree Edges", "Forward Edges", "Back Edges", "Cross Edges"]
for i in range(4):
print(edgeNames[i] + ":", end=" ")
for edge in edges[i]:
print(f"{edge[0]}->{edge[1]}", end=" ")
print()
// C# program to identify Tree, Back,
// Edge and Cross Edges in DFS of Graph
using System;
using System.Collections.Generic;
class GfG {
static void dfs(int u, int[][] adj, bool[] visited,
bool[] inStack, int[] discovery, int[] finish,
int[] timeStamp, List<int[]>[] edges) {
visited[u] = true;
inStack[u] = true;
discovery[u] = ++timeStamp[0];
foreach (int v in adj[u]) {
if (!visited[v]) {
// Tree Edge
edges[0].Add(new int[]{u, v});
dfs(v, adj, visited, inStack,
discovery, finish, timeStamp, edges);
} else {
if (inStack[v]) {
// Back Edge
edges[2].Add(new int[]{u, v});
} else if (discovery[u] < discovery[v]) {
// Forward Edge
edges[1].Add(new int[]{u, v});
} else {
// Cross Edge
edges[3].Add(new int[]{u, v});
}
}
}
inStack[u] = false;
finish[u] = ++timeStamp[0];
}
static List<int[]>[] classifyEdges(int[][] adj) {
int n = adj.Length;
int[] discovery = new int[n];
int[] finish = new int[n];
bool[] visited = new bool[n];
bool[] inStack = new bool[n];
int[] timeStamp = {0};
List<int[]>[] edges = new List<int[]>[4];
for (int i = 0; i < 4; i++) {
edges[i] = new List<int[]>();
}
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(i, adj, visited, inStack, discovery, finish, timeStamp, edges);
}
}
return edges;
}
static void Main() {
int[][] adj = {
new int[] {1, 2, 7},
new int[] {3},
new int[] {4},
new int[] {5},
new int[] {3, 6, 7},
new int[] {1},
new int[] {},
new int[] {}
};
List<int[]>[] edges = classifyEdges(adj);
string[] edgeNames = {"Tree Edges", "Forward Edges",
"Back Edges", "Cross Edges"};
for (int i = 0; i < 4; i++) {
Console.Write(edgeNames[i] + ": ");
foreach (var edge in edges[i]) {
Console.Write(edge[0] + "->" + edge[1] + " ");
}
Console.WriteLine();
}
}
}
// JavaScript program to identify Tree, Back,
// Edge and Cross Edges in DFS of Graph
function dfs(u, adj, visited, inStack, discovery,
finish, timeStamp, edges) {
visited[u] = true;
inStack[u] = true;
discovery[u] = ++timeStamp.value;
for (let v of adj[u]) {
if (!visited[v]) {
// Tree Edge
edges[0].push([u, v]);
dfs(v, adj, visited, inStack, discovery,
finish, timeStamp, edges);
} else {
if (inStack[v]) {
// Back Edge
edges[2].push([u, v]);
} else if (discovery[u] < discovery[v]) {
// Forward Edge
edges[1].push([u, v]);
} else {
// Cross Edge
edges[3].push([u, v]);
}
}
}
inStack[u] = false;
finish[u] = ++timeStamp.value;
}
function classifyEdges(adj) {
let n = adj.length;
// Array to store discovery timeStamp of node
let discovery = new Array(n).fill(0);
// Array to store finish timeStamp of node
let finish = new Array(n).fill(0);
// Array to check if node is visited
let visited = new Array(n).fill(false);
// Array to check if node is in call stack
let inStack = new Array(n).fill(false);
let timeStamp = { value: 0 };
// Initialize result array with 4
// empty arrays for each edge type
let edges = [[], [], [], []];
// Run DFS for each unvisited vertex
for (let i = 0; i < n; i++) {
if (!visited[i]) {
dfs(i, adj, visited, inStack,
discovery, finish, timeStamp, edges);
}
}
return edges;
}
let adj = [
[1, 2, 7],
[3],
[4],
[5],
[3, 6, 7],
[1],
[],
[]
];
let edges = classifyEdges(adj);
let edgeNames = ["Tree Edges", "Forward Edges", "Back Edges", "Cross Edges"];
for (let i = 0; i < 4; i++) {
let result = edgeNames[i] + ": ";
for (let edge of edges[i]) {
result += edge[0] + "->" + edge[1] + " ";
}
console.log(result);
}
Output
Tree Edges: 0->1 1->3 3->5 0->2 2->4 4->6 4->7 Forward Edges: 0->7 Back Edges: 5->1 Cross Edges: 4->3
Time Complexity: O(V+E), where V is the number of nodes and E is the number of edges.
Auxiliary Space: O(V+E), due to recursive stack space, auxiliary arrays (discovery, finish, visited and inStack) and resultant array.
