Skip to content

Commit 5ef1199

Browse files
committed
change code to use single dfs function (adamant-pwn's suggestion) and create test
1 parent 6a8c14f commit 5ef1199

File tree

2 files changed

+86
-42
lines changed

2 files changed

+86
-42
lines changed

src/graph/strongly-connected-components.md

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -72,72 +72,66 @@ The runtime complexity of the algorithm is $O(n + m)$, because depth first searc
7272
Finally, it is appropriate to mention [topological sort](topological-sort.md) here. In step 2, the algorithm finds strongly connected components in decreasing order of their exit times. Thus, it finds components - vertices of the condensation graph - in an order corresponding to a topological sort of the condensation graph.
7373

7474
## Implementation
75-
```cpp
76-
vector<vector<int>> adj, adj_rev; // adjacency lists of G and G^T
77-
vector<bool> visited; // keeps track of which nodes are already visited
78-
vector<int> order; // sorted list of G's vertices by exit time
79-
vector<int> component; // stores one strongly connected component at a time
80-
81-
void dfs1(int v) {
75+
```{.cpp file=strongly_connected_components}
76+
vector<bool> visited; // keeps track of which nodes are already visited
77+
78+
// runs depth first search starting at vertex v.
79+
// each visited vertex is appended to the output vector when dfs leaves it.
80+
void dfs(int v, vector<vector<int>> &adj, vector<int> &output) {
8281
visited[v] = true;
8382
for (auto u : adj[v])
8483
if (!visited[u])
85-
dfs1(u);
86-
87-
order.push_back(v);
84+
dfs(u, adj, output);
85+
output.push_back(v);
8886
}
89-
90-
void dfs2(int v) {
91-
visited[v] = true;
92-
component.push_back(v);
9387

94-
for (auto u : adj_rev[v])
95-
if (!visited[u])
96-
dfs2(u);
97-
}
98-
99-
int main() {
100-
int n = ...;
101-
for ( ... ) {
102-
int a = ..., b = ...;
103-
adj[a].push_back(b);
104-
adj_rev[b].push_back(a);
105-
}
106-
88+
// input: adj -- adjacency list of G
89+
// output: components -- the strongy connected components in G
90+
// output: adj_scc -- adjacency list of G^SCC (by root nodes)
91+
void strongy_connected_components(vector<vector<int>> &adj,
92+
vector<vector<int>> &components,
93+
vector<vector<int>> &adj_scc) {
94+
int n = adj.size();
95+
components.clear(), adj_scc.clear();
96+
97+
// create adjacency list of G^T
98+
vector<vector<int>> adj_rev(n);
99+
for (int v = 0; v < n; v++)
100+
for (int u : adj[v])
101+
adj_rev[u].push_back(v);
102+
103+
vector<int> order; // will be a sorted list of G's vertices by exit time
104+
107105
visited.assign(n, false);
108106

109107
// first series of depth first searches
110108
for (int i = 0; i < n; i++)
111109
if (!visited[i])
112-
dfs1(i);
110+
dfs(i, adj, order);
113111

114112
visited.assign(n, false);
115113
reverse(order.begin(), order.end());
116114

117-
// condensation graph (explained in text below)
118-
vector<int> root_nodes;
119-
vector<int> roots(n, 0);
120-
vector<vector<int>> adj_scc(n);
121-
115+
vector<int> roots(n, 0); // gives the root vertex of a vertex's SCC
116+
122117
// second series of depth first searches
123118
for (auto v : order)
124119
if (!visited[v]) {
125-
dfs2(v);
120+
std::vector<int> component;
121+
dfs(v, adj_rev, component);
122+
sort(component.begin(), component.end());
123+
components.push_back(component);
126124
int root = component.front();
127-
for (auto u : component) roots[u] = root;
128-
root_nodes.push_back(root);
129-
component.clear();
125+
for (auto u : component)
126+
roots[u] = root;
130127
}
131-
132-
128+
133129
// add edges to condensation graph
130+
adj_scc.assign(n, {});
134131
for (int v = 0; v < n; v++)
135132
for (auto u : adj[v])
136133
if (roots[v] != roots[u])
137134
adj_scc[roots[v]].push_back(roots[u]);
138-
139-
// finished finding SCCs and constructing condensation graph.
140-
.......
141135
}
142136
```
143137
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include <bits/stdc++.h>
2+
using namespace std;
3+
4+
#include "strongly_connected_components.h"
5+
6+
int main() {
7+
// we only have a single test case for now
8+
int n = 10;
9+
vector<vector<int>> adj(n);
10+
adj[0].push_back(1);
11+
adj[0].push_back(7);
12+
adj[1].push_back(1);
13+
adj[1].push_back(2);
14+
adj[2].push_back(1);
15+
adj[2].push_back(5);
16+
adj[3].push_back(2);
17+
adj[3].push_back(4);
18+
adj[4].push_back(9);
19+
adj[5].push_back(3);
20+
adj[5].push_back(6);
21+
adj[5].push_back(9);
22+
adj[6].push_back(2);
23+
adj[7].push_back(0);
24+
adj[7].push_back(6);
25+
adj[7].push_back(8);
26+
adj[8].push_back(6);
27+
adj[8].push_back(9);
28+
adj[9].push_back(4);
29+
30+
vector<vector<int>> components, adj_scc;
31+
strongy_connected_components(adj, components, adj_scc);
32+
33+
// sort things to make it easier to verify
34+
sort(components.begin(), components.end(),
35+
[](auto &l, auto &r) { return l[0] < r[0]; });
36+
for (vector<int> a : adj_scc)
37+
sort(a.begin(), a.end());
38+
39+
assert(components.size() == 4);
40+
assert(components[0] == std::vector<int>({0, 7}));
41+
assert(components[1] == std::vector<int>({1, 2, 3, 5, 6}));
42+
assert(components[2] == std::vector<int>({4, 9}));
43+
assert(components[3] == std::vector<int>({8}));
44+
45+
assert(adj_scc[0] == std::vector<int>({1, 1, 8}));
46+
assert(adj_scc[1] == std::vector<int>({4, 4}));
47+
assert(adj_scc[8] == std::vector<int>({1, 4}));
48+
49+
return 0;
50+
}

0 commit comments

Comments
 (0)