Skip to content
Bill Liao
Go back

Friend Circles

Edit page

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:

Approach: Union-Find (Disjoint Set)

Algorithm

  1. Initialize each city as its own set with a parent array
  2. Iterate over the upper triangle of the matrix; when isConnected[i][j] == 1, union sets i and j
  3. Count the number of distinct roots among all cities

Time & Space Complexity

Java Implementation

public class FriendCircles {

    private int[] parent;
    private int[] rank;

    /**
     * Return the total number of provinces (friend circles).
     * @param isConnected n x n adjacency matrix
     * @return Number of connected components
     */
    public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        parent = new int[n];
        rank = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }

        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (isConnected[i][j] == 1) {
                    union(i, j);
                }
            }
        }

        int count = 0;
        for (int i = 0; i < n; i++) {
            if (find(i) == i) {
                count++;
            }
        }
        return count;
    }

    private int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    private void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX == rootY) {
            return;
        }
        if (rank[rootX] < rank[rootY]) {
            parent[rootX] = rootY;
        } else if (rank[rootX] > rank[rootY]) {
            parent[rootY] = rootX;
        } else {
            parent[rootY] = rootX;
            rank[rootX]++;
        }
    }

    // Test method
    public static void main(String[] args) {
        FriendCircles fc = new FriendCircles();
        int[][] isConnected1 = {{1, 1, 0}, {1, 1, 0}, {0, 0, 1}};
        System.out.println("Input: isConnected = [[1,1,0],[1,1,0],[0,0,1]]");
        System.out.println("Output: " + fc.findCircleNum(isConnected1)); // Expected: 2

        int[][] isConnected2 = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
        System.out.println("Input: isConnected = [[1,0,0],[0,1,0],[0,0,1]]");
        System.out.println("Output: " + fc.findCircleNum(isConnected2)); // Expected: 3
    }
}

Example Walkthrough

For isConnected = [[1,1,0],[1,1,0],[0,0,1]]:

  1. Union(0,1) because isConnected[0][1] == 1. Cities 0 and 1 are now in the same set
  2. No other pairs are connected
  3. Roots: city 0 (representing {0,1}) and city 2 -> 2 provinces

Key Points

  1. Union-Find: Models connectivity by merging sets; ideal for counting connected components
  2. Path Compression: find flattens the tree, making subsequent lookups nearly O(1)
  3. Union by Rank: Attaches the shorter tree under the taller one to keep trees shallow
  4. Upper Triangle Only: The matrix is symmetric, so checking j > i halves the work
  5. Root Count: A node is a root when parent[i] == i, giving the province count

Edit page
Share this post:

Previous Post
Accounts Merge
Next Post
Redundant Connection