[Medium] 547. Number of Provinces
There are n cities. Some of them are connected, while some are not. If city a is connected directly with city b, and city b is connected directly with city c, then city a is connected indirectly with city c.
A province is a group of directly or indirectly connected cities and no other cities outside of the group.
You are given an n x n matrix isConnected where isConnected[i][j] = 1 if the ith city and the jth city are directly connected, and isConnected[i][j] = 0 otherwise.
Return the total number of provinces.
Examples
Example 1:
Input: isConnected = [[1,1,0],[1,1,0],[0,0,1]]
Output: 2
Example 2:
Input: isConnected = [[1,0,0],[0,1,0],[0,0,1]]
Output: 3
Constraints
1 <= n <= 200n == isConnected.lengthn == isConnected[i].lengthisConnected[i][j]is1or0.isConnected[i][i] == 1isConnected[i][j] == isConnected[j][i]
Thinking Process
- Connected Components: Provinces are connected components in the graph
- Model entities as nodes and relationships as edges.
- Pick traversal (BFS/DFS) or shortest-path (Dijkstra) based on weights.
- Union-Find helps when connectivity updates are frequent.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Recursive DFS (this problem) | O(n) | O(h) stack | Natural for trees and graphs |
| Iterative DFS (stack) | O(n) | O(n) | Avoid recursion depth limits |
| DFS with memoization | O(n) | O(n) | Overlapping subproblems on graphs |
| Backtracking DFS | O(2^n) typical | O(n) | Enumerate choices with pruning |
Solution
Time Complexity: O(n² × α(n)) where α is the inverse Ackermann function
Space Complexity: O(n) - For parent array
This solution uses Union-Find to group connected cities into provinces.
class Solution:
def find(self, parent, x):
if parent[x] != x:
parent[x] = self.find(parent, parent[x])
return parent[x]
def unite(self, parent, x, y):
rootX = self.find(parent, x)
rootY = self.find(parent, y)
parent[rootX] = rootY
def findCircleNum(self, isConnected):
n = len(isConnected)
parent = list(range(n))
# Union all connected cities
for i in range(n):
for j in range(n):
if isConnected[i][j] == 1:
self.unite(parent, i, j)
# Count roots
circles = 0
for i in range(n):
if self.find(parent, i) == i:
circles += 1
return circles
Solution Explanation
Approach: Recursive DFS (this problem)
Key idea: 1. Connected Components: Provinces are connected components in the graph
How the code works:
- Connected Components: Provinces are connected components in the graph
- Model entities as nodes and relationships as edges.
- Pick traversal (BFS/DFS) or shortest-path (Dijkstra) based on weights.
- Union-Find helps when connectivity updates are frequent.
Walkthrough — input isConnected = [[1,1,0],[1,1,0],[0,0,1]], expected output 2:
- Initialize variables from the problem setup.
- Apply the main loop / recursion until the condition is met.
- Confirm the result matches the expected output.
| Solution | Time | Space | Notes |
|---|---|---|---|
| Union-Find | O(n² × α(n)) | O(n) | Path compression makes α(n) ≈ constant |
| DFS | O(n²) | O(n) | Simple and intuitive |
| BFS | O(n²) | O(n) | Iterative, no recursion |
How Solution 1 Works
- Initialization: Each city starts as its own parent (separate province)
- Union Operation: For each connection
isConnected[i][j] == 1, unite citiesiandj - Path Compression: The
findfunction compresses paths for efficiency - Count Provinces: Count the number of roots (cities where
parent[i] == i)
Key Insight
A province is a connected component. Union-Find groups all connected cities under the same root, so counting roots gives the number of provinces.
Example Walkthrough
Input: isConnected = [[1,1,0],[1,1,0],[0,0,1]]
Solution 1 (Union-Find):
Initial: parent = [0, 1, 2]
Process connections:
i=0, j=0: isConnected[0][0] = 1 → unite(0, 0) → no change
i=0, j=1: isConnected[0][1] = 1 → unite(0, 1)
find(0) = 0, find(1) = 1
parent[0] = 1
parent = [1, 1, 2]
i=1, j=0: isConnected[1][0] = 1 → unite(1, 0)
find(1) = 1, find(0) = find(1) = 1
Already connected, no change
i=1, j=1: isConnected[1][1] = 1 → unite(1, 1) → no change
i=2, j=2: isConnected[2][2] = 1 → unite(2, 2) → no change
Final: parent = [1, 1, 2]
Count roots: parent[1] == 1, parent[2] == 2
Result: 2 provinces
Solution 2 (DFS):
visited = [false, false, false]
i=0: Not visited
DFS(0):
Mark 0 as visited
Check j=0: isConnected[0][0]=1, visited[0]=true, skip
Check j=1: isConnected[0][1]=1, visited[1]=false
DFS(1):
Mark 1 as visited
Check j=0: visited[0]=true, skip
Check j=1: visited[1]=true, skip
Check j=2: isConnected[1][2]=0, skip
Check j=2: isConnected[0][2]=0, skip
provinces = 1
i=1: Already visited, skip
i=2: Not visited
DFS(2):
Mark 2 as visited
Check j=0: isConnected[2][0]=0, skip
Check j=1: isConnected[2][1]=0, skip
Check j=2: visited[2]=true, skip
provinces = 2
Result: 2 provinces
Complexity
| Solution | Time | Space | Notes | |———-|——|——-|——-| | Union-Find | O(n² × α(n)) | O(n) | Path compression makes α(n) ≈ constant | | DFS | O(n²) | O(n) | Simple and intuitive | | BFS | O(n²) | O(n) | Iterative, no recursion |
Common Mistakes
- Single city:
[[1]]→ return 1 - All connected: All cities in one province → return 1
- None connected: Each city is separate → return n
-
Self-loops:
isConnected[i][i] == 1(handled automatically) - Not using path compression: Leads to O(n) find operations
- Wrong root counting: Counting
parent[i] == ibefore path compression - Symmetric matrix: Only need to check upper or lower triangle (but checking all is fine)
- Visited array: In DFS/BFS, forgetting to mark visited
Optimization Tips
- Path Compression: Essential for Union-Find efficiency
- Union by Rank: Can add rank optimization for better performance
- Early Exit: Can optimize by only checking upper triangle (but code is simpler checking all)
Related Problems
- 200. Number of Islands - Connected components in grid
- 695. Max Area of Island - Find largest component
- 130. Surrounded Regions - Mark connected regions
- 990. Satisfiability of Equality Equations - Union-Find for equality
Pattern Recognition
This problem demonstrates the “Connected Components” pattern:
1. Use Union-Find or DFS/BFS to group connected nodes
2. Count the number of distinct groups
3. Each group represents one component
Similar problems:
- Number of Islands
- Max Area of Island
- Friend Circles (same problem)
- Accounts Merge
References
- LC 547: Number of Provinces on LeetCode
- LeetCode Discuss — LC 547: Number of Provinces
- LeetCode Editorial (may require premium)
Key Takeaways
- Connected Components: Provinces are connected components in the graph
- Symmetric Matrix:
isConnected[i][j] == isConnected[j][i](undirected graph) - Self-loops:
isConnected[i][i] == 1(each city connects to itself) - Union-Find Efficiency: Path compression makes find operations nearly O(1)