Graphs - DFS · Medium
LeetCode 547: Number of Provinces — solution
Solve LeetCode 547 by counting connected components: start a DFS from every unvisited city and count how many times you start — O(n²) time, O(n) space.
Problem in plain words
There are n cities. You get an n × n matrix where isConnected[i][j] = 1 means cities i and j have a direct road (the matrix is symmetric and the diagonal is all 1). Cities that can reach each other directly or through other cities form one province. Return how many provinces there are.
Example: with four cities where 0–1 and 2–3 are linked and nothing else, there are two provinces: {0, 1} and {2, 3}.
Intuition
A province is a connected component of an undirected graph given as an adjacency matrix. Every time you find a city that has not been visited yet, it belongs to a brand-new component: count it, then flood-fill the whole component with a DFS so none of its cities triggers another count.
Approach
- Keep a
visitedarray of sizenand a counterprovinces = 0. - Loop over every city
i. If it is already visited, skip it. - Otherwise increment
provincesand run a DFS fromi. - In the DFS, scan row
cityof the matrix; for everyjwithisConnected[city][j] == 1that is unvisited, mark it and continue the DFS from it. - Return
provinces.
Dry run
Edges 0–1 and 2–3, n = 4.
| Outer i | Visited? | Action | provinces |
|---|---|---|---|
| 0 | no | DFS marks 0, 1 | 1 |
| 1 | yes | skip | 1 |
| 2 | no | DFS marks 2, 3 | 2 |
| 3 | yes | skip | 2 |
Java solution
class Solution {
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
boolean[] visited = new boolean[n];
int provinces = 0;
for (int i = 0; i < n; i++) {
if (!visited[i]) {
provinces++;
visited[i] = true;
dfs(isConnected, visited, i);
}
}
return provinces;
}
private void dfs(int[][] isConnected, boolean[] visited, int city) {
for (int next = 0; next < isConnected.length; next++) {
if (isConnected[city][next] == 1 && !visited[next]) {
visited[next] = true;
dfs(isConnected, visited, next);
}
}
}
}Python solution
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
visited = [False] * n
provinces = 0
for start in range(n):
if visited[start]:
continue
provinces += 1
visited[start] = True
stack = [start]
while stack:
city = stack.pop()
for other in range(n):
if isConnected[city][other] == 1 and not visited[other]:
visited[other] = True
stack.append(other)
return provincesComplexity
| Measure | Cost | Why |
|---|---|---|
| Time | O(n²) | Each city's row of the matrix is scanned exactly once. |
| Space | O(n) | The visited array plus a recursion depth or stack of at most n. |
Edge cases and common mistakes
- Counting edges or 1s — the answer is the number of components, not the number of roads.
- Counting inside the DFS — increment the counter only in the outer loop, when you start a new component.
- Isolated cities — a city connected only to itself is its own province; the outer loop handles it.
- Treating the matrix as directed — it is symmetric, so scanning one row per city is enough.
›How does Union-Find solve it?
Start with n separate sets. For every pair i < j with a road, union them; each successful union (two different roots merged) reduces the component count by one. See Union-Find. With path compression and union by rank it is O(n² · α(n)), essentially the same as DFS here.
›Is BFS fine too?
Yes. Any traversal that marks the whole component before the outer loop moves on gives the same count.
