Opening the reading…
Opening the reading…
GRAPHS › DFS AND BFS ON GRAPHS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A connected component is the set of vertices reachable from one starting vertex. DFS follows edges as far as possible, so one DFS started at vertex start visits exactly every vertex connected to start and no vertex in another component. The visited array makes this statement stable even when the graph contains cycles, repeated edges, or several different paths to the same vertex.
A single DFS is not enough because the graph may be disconnected. Scan vertices from 0 upward; when the scan reaches an unseen vertex, it must be the smallest vertex of a new component, so starting DFS there also gives the components their required outer order. DFS itself may discover vertices in adjacency-list order, so sort the finished component before storing it.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(V + E + sum of c_i log c_i) | Building the adjacency list stores two entries per edge, the outer scan checks V vertices, and DFS examines each stored adjacency entry at most once, which is 2E entries. Sorting is performed once per component, costing the stated sum; in the worst shape, one component contains all V vertices, so sorting adds O(V log V). |
| Space | O(V + E) extra | The adjacency list stores two neighbor entries per edge, while visited, the explicit stack, and one current component use O(V) additional space. The returned 2D array is required output and is excluded; the bound remains O(V + E) in the worst case, including a graph with one large component or many isolated vertices. |
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<int>> depthFirstSearch(int V, int E, vector<vector<int>> &edges) {
vector<vector<int>> adj(V);
int usable = min(E, static_cast<int>(edges.size()));
for (int i = 0; i < usable; ++i) {
int a = edges[i][0];
int b = edges[i][1];
adj[a].push_back(b);
adj[b].push_back(a);
}
vector<int> visited(V, 0);
vector<vector<int>> answer;
for (int start = 0; start < V; ++start) {
if (visited[start]) continue;
vector<int> component;
vector<int> stack;
stack.push_back(start);
visited[start] = 1;
while (!stack.empty()) {
int u = stack.back();
stack.pop_back();
component.push_back(u);
for (int v : adj[u]) {
if (!visited[v]) {
visited[v] = 1;
stack.push_back(v);
}
}
}
sort(component.begin(), component.end());
answer.push_back(component);
}
return answer;
}
};The key placement is visited[v] = 1 when v is pushed, not when it is popped. Suppose two already discovered vertices both point to v. The first push reserves v immediately, so the second edge does nothing. This keeps the stack proportional to the number of vertices instead of allowing repeated edges and cycles to create duplicate pending entries.
A recursive DFS is a reasonable alternative because the graph's reachability rule naturally repeats the same operation for each neighbor. It is not asymptotically better: both versions take O(V + E) before sorting and use O(V) extra space in the worst case. The difference is where the pending work lives. The iterative version stores it in vector stack; recursion stores it in runtime call frames and can be less safe on a deep component.
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
void dfs(int u, const vector<vector<int>> &adj, vector<int> &visited, vector<int> &component) {
visited[u] = 1;
component.push_back(u);
for (int v : adj[u]) {
if (!visited[v]) {
dfs(v, adj, visited, component);
}
}
}
public:
vector<vector<int>> depthFirstSearch(int V, int E, vector<vector<int>> &edges) {
vector<vector<int>> adj(V);
int usable = min(E, static_cast<int>(edges.size()));
for (int i = 0; i < usable; ++i) {
int a = edges[i][0];
int b = edges[i][1];
adj[a].push_back(b);
adj[b].push_back(a);
}
vector<int> visited(V, 0);
vector<vector<int>> answer;
for (int start = 0; start < V; ++start) {
if (visited[start]) continue;
vector<int> component;
dfs(start, adj, visited, component);
sort(component.begin(), component.end());
answer.push_back(component);
}
return answer;
}
};