Consider a directed graph given in below, DFS of the below graph is 1 2 4 6 3 5 7 8. In below diagram if DFS is applied on this graph a tree is obtained which is connected using green edges.
- 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.
Time Complexity(DFS):
Since all the nodes and vertices are visited, the average time complexity for DFS on a graph is O(V + E), where V is the number of vertices and E is the number of edges. In case of DFS on a tree, the time complexity is O(V), where V is the number of nodes.
Algorithm(DFS):
- Pick any node. If it is unvisited, mark it as visited and recur on all its adjacent nodes.
- Repeat until all the nodes are visited, or the node to be searched is found.
Example: Implement DFS using an adjacency list take a directed graph of size n=10, and randomly select the number of edges in the graph varying from 9 to 45. Identify each edge as the forwarding edge, tree edge, back edge, and cross edge.
C++
// C++ #include <bits/stdc++.h> #include <cstdlib> #include <ctime> Â
using namespace std; Â
class Graph { Â
public :     // instance variables     int time = 0;     vector< int > traversal_array;     int v;     int e;     vector<vector< int >> graph_list;     vector<vector< int >> graph_matrix; Â
    Graph( int v)     {         // v is the number of nodes/vertices         this ->v = v;         // e is the number of edge (randomly chosen between 9 to 45)         this ->e = rand () % (45 - 9 + 1) + 9;         // adj. list for graph         this ->graph_list.resize(v);         // adj. matrix for graph         this ->graph_matrix.resize(v, vector< int >(v, 0));     } Â
    // function to create random graph     void create_random_graph()     {         // add edges upto e         for ( int i = 0; i < this ->e; i++)         {             // choose src and dest of each edge randomly             int src = rand () % this ->v;             int dest = rand () % this ->v;             // re-choose if src and dest are same or src and dest already has an edge             while (src == dest && this ->graph_matrix[src][dest] == 1)             {                 src = rand () % this ->v;                 dest = rand () % this ->v;             }             // add the edge to graph             this ->graph_list[src].push_back(dest);             this ->graph_matrix[src][dest] = 1;         }     } Â
    // function to print adj list     void print_graph_list()     {         cout << "Adjacency List Representation:" << endl;         for ( int i = 0; i < this ->v; i++)         {             cout << i << "-->" ;             for ( int j = 0; j < this ->graph_list[i].size(); j++)             {                 cout << this ->graph_list[i][j] << " " ;             }             cout << endl;         }         cout << endl;     } Â
    // function to print adj matrix     void print_graph_matrix()     {         cout << "Adjacency Matrix Representation:" << endl;         for ( int i = 0; i < this ->graph_matrix.size(); i++)         {             for ( int j = 0; j < this ->graph_matrix[i].size(); j++)             {                 cout << this ->graph_matrix[i][j] << " " ;             }             cout << endl;         }         cout << endl;     } Â
    // function the get number of edges     int number_of_edges()     {         return this ->e;     } Â
    // function for dfs     void dfs()     {         this ->visited.resize( this ->v);         this ->start_time.resize( this ->v);         this ->end_time.resize( this ->v);         fill( this ->visited.begin(), this ->visited.end(), false ); Â
        for ( int node = 0; node < this ->v; node++)         {             if (! this ->visited[node])             {                 this ->traverse_dfs(node);             }         }         cout << endl;         cout << "DFS Traversal: " ;         for ( int i = 0; i < this ->traversal_array.size(); i++)         {             cout << this ->traversal_array[i] << " " ;         }         cout << endl;     } Â
    void traverse_dfs( int node)     {         // mark the node visited         this ->visited[node] = true ;         // add the node to traversal         this ->traversal_array.push_back(node);         // get the starting time         this ->start_time[node] = this -> time ;         // increment the time by 1         this -> time ++;         // traverse through the neighbours         for ( int neighbour = 0; neighbour < this ->graph_list[node].size(); neighbour++)         {             // if a node is not visited             if (! this ->visited[ this ->graph_list[node][neighbour]])             {                 // marks the edge as tree edge                 cout << "Tree Edge:" << node << "-->" << this ->graph_list[node][neighbour] << endl;                 // dfs from that node                 this ->traverse_dfs( this ->graph_list[node][neighbour]);             }             else             {                 // when the parent node is traversed after the neighbour node                 if ( this ->start_time[node] > this ->start_time[ this ->graph_list[node][neighbour]] && this ->end_time[node] < this ->end_time[ this ->graph_list[node][neighbour]])                 {                     cout << "Back Edge:" << node << "-->" << this ->graph_list[node][neighbour] << endl;                 }                 // when the neighbour node is a descendant but not a part of tree                 else if ( this ->start_time[node] < this ->start_time[ this ->graph_list[node][neighbour]] && this ->end_time[node] > this ->end_time[ this ->graph_list[node][neighbour]])                 {                     cout << "Forward Edge:" << node << "-->" << this ->graph_list[node][neighbour] << endl;                 }                 // when parent and neighbour node do not have any ancestor and a descendant relationship between them                 else if ( this ->start_time[node] > this ->start_time[ this ->graph_list[node][neighbour]] && this ->end_time[node] > this ->end_time[ this ->graph_list[node][neighbour]])                 {                     cout << "Cross Edge:" << node << "-->" << this ->graph_list[node][neighbour] << endl;                 }             }             this ->end_time[node] = this -> time ;             this -> time ++;         }     } Â
private : Â Â Â Â vector< bool > visited; Â Â Â Â vector< int > start_time; Â Â Â Â vector< int > end_time; }; Â
int main() { Â Â Â Â srand ( time (NULL)); Â Â Â Â int n = 10; Â Â Â Â Graph g(n); Â Â Â Â g.create_random_graph(); Â Â Â Â g.print_graph_list(); Â Â Â Â g.print_graph_matrix(); Â Â Â Â g.dfs(); Â Â Â Â return 0; } Â
// This code is contributed by akashish__ |
Python3
# code import random Â
Â
class Graph:     # instance variables     def __init__( self , v):         # v is the number of nodes/vertices         self .time = 0         self .traversal_array = []         self .v = v         # e is the number of edge (randomly chosen between 9 to 45)         self .e = random.randint( 9 , 45 )         # adj. list for graph         self .graph_list = [[] for _ in range (v)]         # adj. matrix for graph         self .graph_matrix = [[ 0 for _ in range (v)] for _ in range (v)] Â
    # function to create random graph     def create_random_graph( self ):         # add edges upto e         for i in range ( self .e):             # choose src and dest of each edge randomly             src = random.randrange( 0 , self .v)             dest = random.randrange( 0 , self .v)             # re-choose if src and dest are same or src and dest already has an edge             while src = = dest and self .graph_matrix[src][dest] = = 1 :                 src = random.randrange( 0 , self .v)                 dest = random.randrange( 0 , self .v)             # add the edge to graph             self .graph_list[src].append(dest)             self .graph_matrix[src][dest] = 1 Â
    # function to print adj list     def print_graph_list( self ):         print ( "Adjacency List Representation:" )         for i in range ( self .v):             print (i, "-->" , * self .graph_list[i])         print () Â
    # function to print adj matrix     def print_graph_matrix( self ):         print ( "Adjacency Matrix Representation:" )         for i in self .graph_matrix:             print (i)         print () Â
    # function the get number of edges     def number_of_edges( self ):         return self .e Â
    # function for dfs     def dfs( self ):         self .visited = [ False ] * self .v         self .start_time = [ 0 ] * self .v         self .end_time = [ 0 ] * self .v Â
        for node in range ( self .v):             if not self .visited[node]:                 self .traverse_dfs(node)         print ()         print ( "DFS Traversal: " , self .traversal_array)         print () Â
    def traverse_dfs( self , node):         # mark the node visited         self .visited[node] = True         # add the node to traversal         self .traversal_array.append(node)         # get the starting time         self .start_time[node] = self .time         # increment the time by 1         self .time + = 1         # traverse through the neighbours         for neighbour in self .graph_list[node]:             # if a node is not visited             if not self .visited[neighbour]:                 # marks the edge as tree edge                 print ( 'Tree Edge:' , str (node) + '-->' + str (neighbour))                 # dfs from that node                 self .traverse_dfs(neighbour)             else :                 # when the parent node is traversed after the neighbour node                 if self .start_time[node] > self .start_time[neighbour] and self .end_time[node] < self .end_time[neighbour]:                     print ( 'Back Edge:' , str (node) + '-->' + str (neighbour))                 # when the neighbour node is a descendant but not a part of tree                 elif self .start_time[node] < self .start_time[neighbour] and self .end_time[node] > self .end_time[neighbour]:                     print ( 'Forward Edge:' , str (node) + '-->' + str (neighbour))                 # when parent and neighbour node do not have any ancestor and a descendant relationship between them                 elif self .start_time[node] > self .start_time[neighbour] and self .end_time[node] > self .end_time[neighbour]:                     print ( 'Cross Edge:' , str (node) + '-->' + str (neighbour))             self .end_time[node] = self .time             self .time + = 1 Â
Â
if __name__ = = "__main__" : Â Â Â Â n = 10 Â Â Â Â g = Graph(n) Â Â Â Â g.create_random_graph() Â Â Â Â g.print_graph_list() Â Â Â Â g.print_graph_matrix() Â Â Â Â g.dfs() |
Javascript
class Graph { Â
  // instance variables   constructor(v)   {        // v is the number of nodes/vertices     this .time = 0;     this .traversal_array = [];     this .v = v;          // e is the number of edge (randomly chosen between 9 to 45)     this .e = Math.floor(Math.random() * (45 - 9 + 1)) + 9;          // adj. list for graph     this .graph_list = Array.from({ length: v }, () => []);          // adj. matrix for graph     this .graph_matrix = Array.from({ length: v }, () =>       Array.from({ length: v }, () => 0)     );   } Â
  // function to create random graph   create_random_graph()   {        // add edges upto e     for (let i = 0; i < this .e; i++)     {            // choose src and dest of each edge randomly      // choose src and dest of each edge randomly       let src = Math.floor(Math.random() * this .v);       let dest = Math.floor(Math.random() * this .v);              // re-choose if src and dest are same or src and dest already has an edge       while (src === dest || this .graph_matrix[src][dest] === 1) {        src = Math.floor(Math.random() * this .v);         dest = Math.floor(Math.random() * this .v);       }              // add the edge to graph       this .graph_list[src].push(dest);       this .graph_matrix[src][dest] = 1;     }   } Â
  // function to print adj list   print_graph_list() {     console.log( "Adjacency List Representation:" + "<br>" );     for (let i = 0; i < this .v; i++) {       console.log(i, "-->" , ... this .graph_list[i]);     }     console.log( "<br>" );   } Â
  // function to print adj matrix   print_graph_matrix() {     console.log( "Adjacency Matrix Representation:" + "<br>" );     for (let i = 0; i < this .graph_matrix.length; i++) {       console.log( this .graph_matrix[i]);     }     console.log( "<br>" );   } Â
  // function the get number of edges   number_of_edges() {     return this .e;   } Â
  // function for dfs   dfs() {     this .visited = Array.from({ length: this .v }, () => false );     this .start_time = Array.from({ length: this .v }, () => 0);     this .end_time = Array.from({ length: this .v }, () => 0); Â
    for (let node = 0; node < this .v; node++) {       if (! this .visited[node]) {         this .traverse_dfs(node);       }     }     console.log();     console.log( "DFS Traversal: " , this .traversal_array+ "<br>" );     console.log();   } Â
// function to traverse the graph using DFS traverse_dfs(node) { Â
// mark the node as visited this .visited[node] = true ; Â
// add the node to the traversal array this .traversal_array.push(node); Â
// get the starting time for the node this .start_time[node] = this .time; Â
// increment the time by 1 this .time += 1; Â
// loop through the neighbours of the node for (let i = 0; i < this .graph_list[node].length; i++) { let neighbour = this .graph_list[node][i]; Â
// if the neighbour node is not visited if (! this .visited[neighbour]) { Â
// mark the edge as a tree edge console.log( "Tree Edge: " + node + "-->" + neighbour+ "<br>" ); Â
// traverse through the neighbour node this .traverse_dfs(neighbour); } else { Â
// if parent node is traversed after the neighbour node if ( this .start_time[node] > this .start_time[neighbour] && this .end_time[node] < this .end_time[neighbour] ) { console.log( "Back Edge: " + node + "-->" + neighbour+ "<br>" ); } Â
// if the neighbour node is a descendant but not a part of the tree else if ( this .start_time[node] < this .start_time[neighbour] && this .end_time[node] > this .end_time[neighbour] ) { console.log( "Forward Edge: " + node + "-->" + neighbour+ "<br>" ); } Â
// if parent and neighbour node do not // have any ancestor and descendant relationship between them else if ( this .start_time[node] > this .start_time[neighbour] && this .end_time[node] > this .end_time[neighbour] ) { console.log( "Cross Edge: " + node + "-->" + neighbour+ "<br>" ); } } } Â
// get the ending time for the node this .end_time[node] = this .time; Â
// increment the time by 1 this .time += 1; } Â
} Â
// main const n = 10; const g = new Graph(n); g.create_random_graph(); g.print_graph_list(); g.print_graph_matrix(); g.dfs(); |
Adjacency List Representation: 0 --> 5 1 --> 3 7 2 --> 4 3 8 9 3 --> 3 4 --> 0 5 --> 2 0 6 --> 0 7 --> 7 4 3 8 --> 8 9 9 --> 9 Adjacency Matrix Representation: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0] [0, 0, 0, 1, 0, 0, 0, 1, 0, 0] [0, 0, 0, 1, 1, 0, 0, 0, 1, 1] [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] [1, 0, 1, 0, 0, 0, 0, 0, 0, 0] [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 1, 1, 0, 0, 1, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 1, 1] [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] Tree Edge: 0-->5 Tree Edge: 5-->2 Tree Edge: 2-->4 Tree Edge: 2-->3 Tree Edge: 2-->8 Tree Edge: 8-->9 Forward Edge: 2-->9 Cross Edge: 5-->0 Back Edge: 1-->3 Tree Edge: 1-->7 Cross Edge: 7-->4 Cross Edge: 7-->3 Back Edge: 6-->0 DFS Traversal: [0, 5, 2, 4, 3, 8, 9, 1, 7, 6]
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!