Count the Separate Friend Circles
Solve this ProblemThere are n people, and some of them are direct friends. The friendships are given as an n × n adjacency matrix: isConnected[i][j] = 1 if person i and person j are direct friends, and 0 otherwise (the matrix is symmetric and every person is a "friend" of themselves on the diagonal). A friend circle is a group of people connected through chains of direct friendships, and it cannot be enlarged with anyone else. Return the number of friend circles.
Equivalently: count the connected components of an undirected graph given as an adjacency matrix.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ n ≤ 20 people, numbered 0 … n − 1 - ◆
isConnected is an n × n adjacency matrix: isConnected[i][j] = 1 means person i and person j are direct friends, 0 means they are not. It is symmetric and isConnected[i][i] = 1 - ◆
Friendship is transitive for circle purposes: a circle is a maximal group of people in which everybody is connected to everybody else through a chain of direct friends (a connected component of the friendship graph) - ◆
Return the number of circles (connected components), counting a person with no friends as a circle of their own
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Keep Lowering Labels Until Nothing Changes
BruteGive every person a label equal to their own number. Then sweep over all pairs again and again: whenever person i is a friend of person j and j carries a smaller label, person i takes that smaller label. Labels spread through friendships, so when a full sweep changes nothing, everyone in a circle carries the same label — the smallest number in the circle. The circles are then exactly the people whose label equals their own number. A single sweep costs O(n²), and information needs up to n sweeps to travel across a long chain of friends: O(n³) in the worst case.
O(n³)O(n)1class Solution {
2 public int countProvinces(int[][] isConnected) {
3 int n = isConnected.length;
4 int[] label = new int[n];
5 for (int i = 0; i < n; i++) label[i] = i;
6 boolean changed = true;
7 while (changed) {
8 changed = false;
9 for (int i = 0; i < n; i++) {
10 for (int j = 0; j < n; j++) {
11 if (isConnected[i][j] == 1 && label[j] < label[i]) {
12 label[i] = label[j];
13 changed = true;
14 }
15 }
16 }
17 }
18 int count = 0;
19 for (int i = 0; i < n; i++) {
20 if (label[i] == i) count++;
21 }
22 return count;
23 }
24}Optimal — Depth-First Search From Every Unvisited Person
OptimalWalk through the people in order. Each time you meet somebody who has not been visited yet, they belong to a circle nobody has counted: count one more circle and explore the whole circle from that person — mark the person visited, then for every friend (a 1 in the person's row of the matrix) that is not visited yet, explore them the same way. Everybody reachable through friendships gets marked, so no circle is counted twice. Every row of the matrix is scanned once: O(n²) time, O(n) for the visited marks and the recursion.
O(n²)O(n)1class Solution {
2 public int countProvinces(int[][] isConnected) {
3 int n = isConnected.length;
4 boolean[] seen = new boolean[n];
5 int count = 0;
6 for (int i = 0; i < n; i++) {
7 if (!seen[i]) {
8 count++;
9 explore(isConnected, seen, i);
10 }
11 }
12 return count;
13 }
14
15 private void explore(int[][] m, boolean[] seen, int i) {
16 seen[i] = true;
17 for (int j = 0; j < m.length; j++) {
18 if (m[i][j] == 1 && !seen[j]) explore(m, seen, j);
19 }
20 }
21}