547. Number of Provinces
On LeetCode ->Reformulated question¶
Given an undirected graph as an n x n adjacency matrix, return how many connected components it has.
Example:
Key trick¶
Treat each city as a graph node.
- Start a DFS/BFS from every unvisited city.
- Each new traversal discovers exactly one province.
Trap¶
- Counting edges instead of connected components.
- Forgetting a
visitedset/list, which causes double counting. - Assuming scanning only the upper triangle is enough for traversal logic.
- Your DFS marks nodes as non-provinces too aggressively and can revisit nodes forever on dense graphs because it has no true
visitedguard.
Why this question is interesting¶
It is a basic graph connectivity problem disguised as a matrix problem.
- It tests whether you can map matrix input to graph traversal.
- It has multiple clean solutions: DFS, BFS, Union-Find.
Solve the problem with idiomatic python¶
class Solution:
def findCircleNum(self, isConnected: list[list[int]]) -> int:
n = len(isConnected)
visited = [False] * n
def dfs(city: int) -> None:
# Visit all cities in the same connected component.
visited[city] = True
for nei in range(n):
if isConnected[city][nei] == 1 and not visited[nei]:
dfs(nei)
provinces = 0
for city in range(n):
# A new unvisited city starts a new province.
if not visited[city]:
provinces += 1
dfs(city)
return provinces
Pytest test¶
import pytest
@pytest.mark.parametrize(
("isConnected", "expected"),
[
([[1]], 1),
([[1, 0], [0, 1]], 2),
([[1, 1], [1, 1]], 1),
([[1, 1, 0], [1, 1, 0], [0, 0, 1]], 2),
([[1, 0, 0], [0, 1, 0], [0, 0, 1]], 3),
([[1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 1]], 2),
([[1, 1, 1], [1, 1, 1], [1, 1, 1]], 1),
],
)
def test_find_circle_num(isConnected, expected):
assert Solution().findCircleNum(isConnected) == expected
Comment my solution¶
Your idea is aiming at component merging, but the implementation is not correct.
- It does not maintain a real
visitedstructure. - It starts outer loops from
1, so city0is never used as a starting point. - It only explores neighbors
k > j, which can miss reachable nodes. - It can recurse repeatedly on already explored nodes.
provinceargument is unused.
A small counterexample:
- Expected:
1 - Your code can keep recursing between nodes without a visited check.
class Solution:
def findCircleNum(self, isConnected: list[list[int]]) -> int:
n = len(isConnected)
provinces = [1] * n
def dfs(province, j):
# j belongs to province
# now we're looking for node directly connected to i
# which implies they belonge province
for k in range(j + 1, n):
if isConnected[j][k]:
# because k connected to i in belong to province
# so k can no longer be a province
provinces[k] = 0
dfs(province, k)
for i in range(1, n):
for j in range(i + 1, n):
if isConnected[i][j]:
# j belongs to province i
provinces[j] = 0
dfs(i, j)
return sum(provinces)