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.

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.

solutions

dfs

Maintain a seen set, and just DFS whenever we see a new node, which is basically just the solution to 200-number-of-islands.

// O(n^2)
class Solution {
	public void dfs(int[][] isConnected, HashSet<Integer> seen, int x) {
		for (int i = 0; i < isConnected.length; i++) {
			if (isConnected[x][i] == 1 && !seen.contains(i)) {
				seen.add(i);
				dfs(isConnected, seen, i);
			}
		}
	}
 
	public int findCircleNum(int[][] isConnected) {
		HashSet<Integer> seen = new HashSet<Integer>();
		int res = 0;
		for (int x = 0; x < isConnected.length; x++) {
			if (!seen.contains(x)) {
				seen.add(x);
				dfs(isConnected, seen, x);
				res += 1;
			}
		}
		return res;
	}
}

union-find

Standard union-find. To avoid doing an extra recursion on the parents, just maintain a running counter of how many components are remaining (and reduce by 1 after every successful union operation).

// O(n^2)
class UnionFind {  
    public ArrayList<Integer> parents;  
    public ArrayList<Integer> sizes;  
  
    public UnionFind(int n) {  
        parents = new ArrayList<>();  
        sizes = new ArrayList<>();  
        for (int i = 0; i < n; i++) {  
            parents.add(i);  
            sizes.add(1);  
        }  
    }  
  
    public boolean union(int u, int v) {  
        int pu = find(u);  // Find root of u  
        int pv = find(v);  // Find root of v  
  
        if (pu == pv) {  
            return false;  // u and v are already in the same set  
        }  
  
        // Union by size: attach the smaller tree to the larger one  
        if (sizes.get(pu) < sizes.get(pv)) {  
            parents.set(pu, pv);  // Attach smaller tree pu to larger tree pv  
            sizes.set(pv, sizes.get(pv) + sizes.get(pu));  // Update size of pv's tree  
        } else {  
            parents.set(pv, pu);  // Attach smaller tree pv to larger tree pu  
            sizes.set(pu, sizes.get(pu) + sizes.get(pv));  // Update size of pu's tree  
        }  
  
        return true;  
    }  
  
    public int find(int u) {  
        if (u != parents.get(u)) {  
            // Path compression step  
            parents.set(u, find(parents.get(u)));  
        }  
        return parents.get(u);  
    }  
}
 
class Solution {  
    public int findCircleNum(int[][] isConnected) {  
        int n = isConnected.length;  
        int res = n;  
        UnionFind uf = new UnionFind(n);  
        for (int i = 0; i < n; i++) {  
            for (int j = 0; j < n; j++) {  
                if (isConnected[i][j] == 1 && uf.union(i, j)) {  
                    res--;  
                }  
            }  
        }  
        return res;  
    }  
}