The transitive closure of a graph is a measure of which vertices are reachable from other vertices. It can be represented as a matrix M, where M[i][j] == 1 if there is a path between vertices i and j, and otherwise 0.
Input: graph =[[0,1,3],[1,2],[2],[3]]Output: [[1,1,1,1],[0,1,1,0],[0,0,1,0],[0,0,0,1]]Explanation:
- From vertex 0: can reach 0(self),1(direct),2(via 1),3(direct)- From vertex 1: can reach 1(self),2(direct), but not 0 or 3- From vertex 2: can only reach 2(self)- From vertex 3: can only reach 3(self)
For each vertex, we can perform a DFS to find all vertices reachable from it. The transitive closure matrix will have M[i][j] = 1 if vertex j is reachable from vertex i.
classSolution {
public: vector<vector<int>> transitiveClosure(vector<vector<int>>& graph) {
int n = graph.size();
vector<vector<int>> ans(n, vector<int>(n, 0));
for (int i =0; i < n; i++) {
vector<bool> vis(n, false);
dfs(graph, i, i, ans, vis);
}
return ans;
}
private:void dfs(vector<vector<int>>& graph, int src, int curr, vector<vector<int>>& ans, vector<bool>& vis) {
vis[curr] = true;
ans[src][curr] =1;
for (int next : graph[curr]) {
if (!vis[next]) {
dfs(graph, src, next, ans, vis);
}
}
}
};
classSolution {
publicint[][]transitiveClosure(int[][] graph) {
int n = graph.length;
int[][] ans =newint[n][n];
for (int i = 0; i < n; i++) {
boolean[] vis =newboolean[n];
dfs(graph, i, i, ans, vis);
}
return ans;
}
privatevoiddfs(int[][] graph, int src, int curr, int[][] ans, boolean[] vis) {
vis[curr]=true;
ans[src][curr]= 1;
for (int next : graph[curr]) {
if (!vis[next]) {
dfs(graph, src, next, ans, vis);
}
}
}
}
classSolution {
funtransitiveClosure(graph: Array<IntArray>): Array<IntArray> {
val n = graph.size
val ans = Array(n) { IntArray(n) }
for (i in0 until n) {
val vis = BooleanArray(n)
dfs(graph, i, i, ans, vis)
}
return ans
}
privatefundfs(graph: Array<IntArray>, src: Int, curr: Int, ans: Array<IntArray>, vis: BooleanArray) {
vis[curr] = true ans[src][curr] = 1for (next in graph[curr]) {
if (!vis[next]) {
dfs(graph, src, next, ans, vis)
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classSolution:
deftransitiveClosure(self, graph: List[List[int]]) -> List[List[int]]:
n = len(graph)
ans = [[0] * n for _ in range(n)]
for i in range(n):
vis = [False] * n
self.dfs(graph, i, i, ans, vis)
return ans
defdfs(self, graph: List[List[int]], src: int, curr: int, ans: List[List[int]], vis: List[bool]) ->None:
vis[curr] =True ans[src][curr] =1for next_node in graph[curr]:
ifnot vis[next_node]:
self.dfs(graph, src, next_node, ans, vis)
⏰ Time complexity: O(V * (V + E)), where V is the number of vertices and E is the number of edges. We perform DFS from each vertex, and each DFS takes O(V + E) time
🧺 Space complexity: O(V²), for the result matrix and O(V) for the visited array and recursion stack
The Floyd-Warshall algorithm can be adapted to compute transitive closure. Instead of finding shortest paths, we determine reachability between all pairs of vertices.
classSolution {
funtransitiveClosure(graph: Array<IntArray>): Array<IntArray> {
val n = graph.size
val ans = Array(n) { IntArray(n) }
// Initialize direct edges
for (i in0 until n) {
for (j in graph[i]) {
ans[i][j] = 1 }
ans[i][i] = 1// Self-loop
}
// Floyd-Warshall for reachability
for (k in0 until n) {
for (i in0 until n) {
for (j in0 until n) {
ans[i][j] = ans[i][j] or (ans[i][k] and ans[k][j])
}
}
}
return ans
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classSolution:
deftransitiveClosure(self, graph: List[List[int]]) -> List[List[int]]:
n = len(graph)
ans = [[0] * n for _ in range(n)]
# Initialize direct edgesfor i in range(n):
for j in graph[i]:
ans[i][j] =1 ans[i][i] =1# Self-loop# Floyd-Warshall for reachabilityfor k in range(n):
for i in range(n):
for j in range(n):
ans[i][j] = ans[i][j] or (ans[i][k] and ans[k][j])
return ans