Open In App

POTD Solutions | 17 Oct’ 23 | Transitive Closure of a Graph

Last Updated : 19 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Welcome to the daily solutions of our PROBLEM OF THE DAY (POTD). We will discuss the entire problem step-by-step and work towards developing an optimized solution. This will not only help you brush up on your concepts of Graphs but will also help you build up problem-solving skills.

POTD 17 October: Transitive closure of a Graph

Given a directed graph, determine whether a vertex j is reachable from another vertex i for all vertex pairs (i, j) in the given graph. Here, vertex j is reachable from another vertex means that there is a path from vertex to j. The reachability matrix is called the transitive closure of a graph. The directed graph is represented by an adjacency matrix where there are N vertices. 

Example 1:

e1

Example

Example 2:

e2

Example

We recommend you to try this problem on our GeeksforGeeks Practice portal first, and maintain your streak to earn Geeksbits and other exciting prizes, before moving towards the solution.

Transitive closure of a Graph using Floyd Warshall Algorithm:

The idea is to use Floyd Warshall Algorithm by intializing the a matrix to represent direct reachability between vertices. Then use the Floyd-Warshall algorithm to update this matrix to reflect whether there exists a path from one vertex to another, considering all possible intermediate vertices. The resulting matrix represents the transitive closure of the input graph.

Follow the steps to solve the above problem:

  • Create a matrix dis of size N x N, where N is the number of vertices in the graph.
  • Iterate through each pair of vertices (i, j) in the graph. If i is equal to j or there is a direct edge from vertex i to vertex j (graph[i][j] == 1), mark dis[i][j] as 1 to indicate direct reachability.
  • Apply the Floyd-Warshall algorithm by using three nested loops to update the ‘dis‘ matrix. For each intermediate vertex k, check if there’s a path from vertex i to k and a path from vertex k to j. If both conditions are met
    (dis[i][k] == 1 and dis[k][j] == 1), set dis[i][j] to 1 to indicate reachability from vertex i to j.
  • Repeat the above step, until all pairs of vertices have been checked, considering all possible intermediate vertices.
  • The di matrix, now represents the transitive closure of the input graph.

Below is the implementation of above approach:

C++




class Solution {
public:
    vector<vector<int> >
    transitiveClosure(int N, vector<vector<int> > graph)
    {
        // Create a 2D vector 'dis' to store the transitive
        // closure matrix.
        vector<vector<int> > dis(N, vector<int>(N, 0));
  
        // Initialize 'dis' matrix based on the given
        // 'graph'.
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                
                // If 'i' and 'j' are the same or there's a
                // direct edge from 'i' to 'j', mark it as
                // reachable.
                if (i == j || graph[i][j] == 1)
                    dis[i][j] = 1;
            }
        }
  
        // Compute the transitive closure using
        // Floyd-Warshall algorithm.
        for (int k = 0; k < N; k++) {
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    
                    // If there's a path from 'i' to 'k' and
                    // from 'k' to 'j', mark 'i' to 'j' as
                    // reachable.
                    if (dis[i][k] == 1 && dis[k][j] == 1) {
                        dis[i][j] = 1;
                    }
                }
            }
        }
  
        return dis;
    }
};


Java




import java.util.ArrayList;
  
class Solution {
    static ArrayList<ArrayList<Integer> >
    transitiveClosure(int N, int[][] graph)
    {
        // Create a 2D ArrayList 'dis' to store the
        // transitive closure matrix.
        ArrayList<ArrayList<Integer> > dis
            = new ArrayList<>(N);
  
        // Initialize 'dis' matrix based on the given
        // 'graph'.
        for (int i = 0; i < N; i++) {
            ArrayList<Integer> row = new ArrayList<>(N);
            for (int j = 0; j < N; j++) {
                // If 'i' and 'j' are the same or there's a
                // direct edge from 'i' to 'j', mark it as
                // reachable.
                if (i == j || graph[i][j] == 1)
                    row.add(1);
                else
                    row.add(0);
            }
            dis.add(row);
        }
  
        // Compute the transitive closure using
        // Floyd-Warshall algorithm.
        for (int k = 0; k < N; k++) {
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    // If there's a path from 'i' to 'k' and
                    // from 'k' to 'j', mark 'i' to 'j' as
                    // reachable.
                    if (dis.get(i).get(k) == 1
                        && dis.get(k).get(j) == 1) {
                        dis.get(i).set(j, 1);
                    }
                }
            }
        }
  
        return dis;
    }
}


Python3




class Solution:
    def transitiveClosure(self, N, graph):
        # Create a 2D list 'dis' to store the transitive closure matrix.
        dis = [[0] * N for _ in range(N)]
  
        # Initialize 'dis' matrix based on the given 'graph'.
        for i in range(N):
            for j in range(N):
                # If 'i' and 'j' are the same or there's a direct edge
                # from 'i' to 'j', mark it as reachable.
                if i == j or graph[i][j] == 1:
                    dis[i][j] = 1
  
        # Compute the transitive closure using Floyd-Warshall algorithm.
        for k in range(N):
            for i in range(N):
                for j in range(N):
                    # If there's a path from 'i' to 'k' and from 'k' to 'j',
                    # mark 'i' to 'j' as reachable.
                    if dis[i][k] == 1 and dis[k][j] == 1:
                        dis[i][j] = 1
  
        return dis


Time Complexity: O(N3), As we are using a nested loop of size N*N*N, So our complexity becomes O(N3).
Auxilary Space: O(N2), As we have made a matrix of size N*N.

Note: The auxiliary space can be optimized to O(1) by doing modification in the input adjacency matrix which will then represent transitive closure of the graph instead of creating new matrix for it.

Transitive closure of a Graph using DFS

The idea is to run a Depth-First Search (DFS) from each vertex. Constructs an adjacency list representation of the graph and then, for each vertex, run a DFS to mark all reachable vertices. The DFS explores and marks all nodes that are reachable from the current node, thus creating a matrix that indicates the reachability of all pairs of nodes in the graph.

Follow the steps to solve the above problem:

  • Construct an adjacency list representation (adj) of the directed graph based on the given adjacency matrix of the graph. It identifies direct edges from vertex i to vertex j.
  • Initialize a N*N matrix (dis) to represent the transitive closure of the given graph.
  • For each vertex i, perform a Depth-First Search (DFS) starting from that vertex. The DFS explores and marks all vertices that are reachable from the current vertex i. During the DFS, it marks the dis matrix to indicate the reachability from vertex i to each of the reachable vertices.
  • Repeat the above step for all vertices in the graph, resulting in a dis matrix that represents the transitive closure of the directed graph, indicating which vertices are reachable from each other.

Below is the implementation of above approach:

C++




class Solution {
public:
    // Depth-First Search (DFS) function to mark reachable
    // vertices from 'curr' in the adjacency list 'adj'.
    void dfs(int i, int curr, vector<vector<int> >& adj,
             vector<vector<int> >& dis, vector<int>& vis)
    {
        // Mark the current vertex curr as visited.
        vis[curr] = 1;
  
        // Mark i to curr as reachable 
        dis[i][curr] = 1;
  
        // Traverse all neighbors of curr in the adjacency
        // list.
        for (auto x : adj[curr]) {
            if (!vis[x]) {
                
                // If the neighbor 'x' has not been visited,
                // recursively explore it.
                dfs(i, x, adj, dis, vis);
            }
        }
    }
  
    vector<vector<int> >
    transitiveClosure(int N, vector<vector<int> > graph)
    {
        // Create an adjacency list 'adj' to represent the
        // graph.
        vector<vector<int> > adj(N);
        
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                
                // Check if there is a direct edge from 'i'
                // to 'j' and 'i' is not equal to 'j'.
                if (graph[i][j] == 1 && i != j) {
                    
                    // Add 'j' to the adjacency list of 'i'
                    // since there is a direct edge from 'i'
                    // to 'j'.
                    adj[i].push_back(j);
                }
            }
        }
  
        // Create a matrix dis to represent the transitive
        // closure with all elements initialized to 0.
        vector<vector<int> > dis(N, vector<int>(N, 0));
  
        // Perform DFS from each vertex i to mark
        // reachable vertices and update the 'dis' matrix.
        for (int i = 0; i < N; i++) {
            
            // Create a vis array to track visited
            // vertices for each DFS call.
            vector<int> vis(N, 0);
            
            // Start DFS from vertex 'i'.
            dfs(i, i, adj, dis, vis);
        }
  
        // Return the dis matrix, which represents the
        // transitive closure of the input graph.
        return dis;
    }
};


Java




import java.util.ArrayList;
  
class Solution {
    // Depth-First Search (DFS) function to mark reachable
    // vertices from 'curr' in the adjacency list 'adj'.
    void dfs(int i, int curr,
             ArrayList<ArrayList<Integer> > adj,
             ArrayList<ArrayList<Integer> > dis, int[] vis)
    {
        // Mark the current vertex curr as visited.
        vis[curr] = 1;
  
        // Mark i to curr as reachable
        dis.get(i).set(curr, 1);
  
        // Traverse all neighbors of curr in the adjacency
        // list.
        for (int x : adj.get(curr)) {
            if (vis[x] == 0) {
                // If the neighbor 'x' has not been visited,
                // recursively explore it.
                dfs(i, x, adj, dis, vis);
            }
        }
    }
  
    ArrayList<ArrayList<Integer> >
    transitiveClosure(int N, int[][] graph)
    {
        // Create an adjacency list 'adj' to represent the
        // graph.
        ArrayList<ArrayList<Integer> > adj
            = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            adj.add(new ArrayList<>());
            for (int j = 0; j < N; j++) {
                // Check if there is a direct edge from 'i'
                // to 'j' and 'i' is not equal to 'j'.
                if (graph[i][j] == 1 && i != j) {
                    // Add 'j' to the adjacency list of 'i'
                    // since there is a direct edge from 'i'
                    // to 'j'.
                    adj.get(i).add(j);
                }
            }
        }
  
        // Create a matrix dis to represent the transitive
        // closure with all elements initialized to 0.
        ArrayList<ArrayList<Integer> > dis
            = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            dis.add(new ArrayList<>());
            for (int j = 0; j < N; j++) {
                dis.get(i).add(0);
            }
        }
  
        // Perform DFS from each vertex i to mark
        // reachable vertices and update the 'dis' matrix.
        for (int i = 0; i < N; i++) {
            // Create a vis array to track visited
            // vertices for each DFS call.
            int[] vis = new int[N];
  
            // Start DFS from vertex 'i'.
            dfs(i, i, adj, dis, vis);
        }
  
        // Return the dis matrix, which represents the
        // transitive closure of the input graph.
        return dis;
    }
}


Python3




class Solution:
    def transitiveClosure(self, N, graph):
        # Helper function to perform Depth-First Search (DFS)
        def dfs(i, curr, adj, dis, vis):
            # Mark the current vertex 'curr' as visited
            vis[curr] = 1
  
            # Mark 'i' to 'curr' as reachable
            dis[i][curr] = 1
  
            # Traverse all neighbors of 'curr' in the adjacency list
            for x in adj[curr]:
                if not vis[x]:
                    # If the neighbor 'x' has not been visited,
                    # recursively explore it
                    dfs(i, x, adj, dis, vis)
  
        # Create an adjacency list 'adj' to represent the graph
        adj = [[] for _ in range(N)]
  
        for i in range(N):
            for j in range(N):
                # Check if there is a direct edge from 'i' to 'j'
                # and 'i' is not equal to 'j'
                if graph[i][j] == 1 and i != j:
                    # Add 'j' to the adjacency list of 'i' since
                    # there is a direct edge from 'i' to 'j'
                    adj[i].append(j)
  
        # Create a matrix 'dis' to represent the transitive closure
        # with all elements initialized to 0
        dis = [[0] * N for _ in range(N)]
  
        # Perform DFS from each vertex 'i' to mark reachable vertices
        # and update the 'dis' matrix
        for i in range(N):
            # Create a 'vis' array to track visited vertices for each DFS call
            vis = [0] * N
  
            # Start DFS from vertex 'i'
            dfs(i, i, adj, dis, vis)
  
        # Return the 'dis' matrix, which represents the transitive closure
        # of the input graph
        return dis


Time Complexity: O(N3), As we are traversing a 2D matrix of size N*N, and and for each node we are using dfs function in the worst case it will which whole matrix for each node.
Auxilary Space: O(N2), As we have made a 2D matrix of size N*N.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads