Leetcode 547 - Number of Provinces
Depth-First Search | Breadth-First Search | Union Find | Graph |
Leetcode Link for 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.
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 <= 200
- n == isConnected.length
- n == isConnected[i].length
- isConnected[i][j] is 1 or 0.
- isConnected[i][i] == 1
- isConnected[i][j] == isConnected[j][i]
We will use three different approaches to solve this problem:
1. Union Find (Disjoint Set)
2. Depth First Search
3. Breadth First Search
Union Find (Disjoint Set):
We will be doing it in two steps:
- 1. Write a class DisjointSet
- 2. Use the unite and getProvinceCout() method from the findCrileNum() method
If you need to brush up your knowledge about disjoint set, below is the link:
Union By Rank and Path Compression in Union-Find (Disjoint Sets)
Implementation using Union Find (Disjoint Set):
package com.spsoft.leetcode.medium.graph;
public class L547NumberOfProvinces_DisjointSet {
DisjointSet ds;
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
ds = new DisjointSet(n);
for(int i=0; i < n; i++){
for(int j=i+1; j < n; j++){
if(isConnected[i][j] == 1)
ds.unite(i, j);
}
}
return ds.getProvinceCount(n);
}
private class DisjointSet{
int n;
int count=0;
int [] parent;
int [] rank;
public DisjointSet(int n){
this.n = n;
parent = new int[n];
rank = new int[n];
for(int i=0; i < n; i++){
parent[i] = i;
}
}
int find(int x){
if(x != parent[x]){
parent[x] = find(parent[x]);
}
return parent[x];
}
void unite(int u, int v){
int i = find(u);
int j = find(v);
if(i == j)
return;
if(rank[i] >= rank[j]){
parent[j] = i;
if(rank[i] == rank[j])
rank[i]++;
}
else {
parent[i] = j;
}
}
int getProvinceCount(int n){
for(int i= 0; i < n; i++){
if(parent[i]==i)
count++;
}
return count;
}
}
}
Implementation using DFS and BFS:
package com.spsoft.leetcode.medium.graph;
import java.util.*;
public class L547NumberOfProvinces_DFS_BFS {
boolean[] visited;
public int findCircleNum(int[][] isConnected) {
int count = 0;
int n = isConnected.length;
visited = new boolean[n];
for (int i = 0; i < n; i++) {
if (!visited[i]) {
count++;
dfs(i, n, isConnected);
//bfs(i, n, isConnected);
}
}
return count;
}
void dfs(int i, int n, int[][] isConnected) {
visited[i] = true;
for (int j = 0; j < n; j++) {
if (!visited[j] && isConnected[i][j] == 1) {
dfs(j, n, isConnected);
}
}
}
void bfs(int city, int n, int[][] isConnected) {
Queue<Integer> q = new ArrayDeque<>();
visited[city] = true;
q.offer(city);
while (!q.isEmpty()) {
Integer u = q.poll();
for (int v = 0; v < n; v++) {
if (!visited[v] && isConnected[u][v] == 1) {
visited[v] = true;
q.offer(v);
}
}
}
}
}
Time and Space Complexity:
-
Disjoint Set (Union-Find):
-
Time Complexity: 𝑂(𝑛2. 𝛼(𝑛))
Union-Find operations (find and unite) are nearly constant time 𝑂(𝛼(𝑛)),
where 𝛼 is the inverse Ackermann function, very slowly growing. Each edge is processed once, leading to 𝑂(𝑛2 .𝛼(𝑛)) complexity.
-
Space Complexity: 𝑂(𝑛)
Memory is used for the parent and rank arrays, both of size 𝑛.
-
-
DFS (Depth-First Search):
- Time Complexity: 𝑂(𝑛2)
The DFS function is called once for each node. Each call processes every edge connected to the node, resulting in an 𝑂(𝑛2) complexity overall.
- Space Complexity: 𝑂(𝑛) , due to the recursion stack and the visited array of size 𝑛.
-
BFS (Breadth-First Search):
-
Time Complexity: 𝑂(𝑛2)
Similar to DFS, BFS processes each node and its edges, leading to an 𝑂(𝑛2) complexity.
-
Space Complexity: 𝑂(𝑛),
Memory is required for the visited array and the queue, holding up to 𝑛 nodes in the worst case.
-
Comparative Summary:
Time Complexity Comparison:
-
Union-Find: 𝑂(𝑛2. 𝛼(𝑛))
-
DFS: 𝑂(𝑛2)
-
BFS: 𝑂(𝑛2)
While DFS and BFS have straightforward 𝑂(𝑛2) time complexity,
the Union-Find approach is slightly more efficient in practice due to the near-constant time complexity of its operations.
Space Complexity Comparison:
All three methods have linear space complexity 𝑂(𝑛).
Author: Mohammad J Iqbal