Count the friend groups
There are n people. isConnected[i][j] is 1 if person
i knows person j and 0 otherwise; the matrix is symmetric and its diagonal is
1. Knowing is shared through friends: if a knows b and b knows c, then a, b and c are in the same group. Return the
number of groups.
Examples
| Input | Output | Why |
|---|---|---|
isConnected = [[1,1,0],[1,1,0],[0,0,1]] | 2 | {0, 1} and {2}. |
isConnected = [[1,0,0],[0,1,0],[0,0,1]] | 3 | Nobody knows anybody else. |
isConnected = [[1,0,0,1],[0,1,1,0],[0,1,1,1],[1,0,1,1]] | 1 | 0 knows 3, 3 knows 2, 2 knows 1: one chain. |
Constraints
- 1 ≤ n ≤ 200
- isConnected[i][j] is 0 or 1
- isConnected[i][i] = 1 and the matrix is symmetric
Hints
Hint 1
The matrix is an adjacency matrix: person i's friends are the columns of row i holding a 1.
Hint 2
Count connected components: every time you meet a person not yet in any group, search from them and mark their whole group.
Hint 3
Union-find works equally well: union every pair (i, j) with a 1, then count the roots.
Where people lose marks
- Counting direct friendships (the number of 1s) instead of groups.
- Reading only the upper triangle and missing connections, or double-counting pairs.
- Forgetting that a person alone is a group of one.
Dry run on [[1,1,1,0,0,0],[1,1,0,0,0,0],[1,0,1,0,0,0],[0,0,0,1,1,0],[0,0,0,1,1,0],[0,0,0,0,0,1]]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 3groups1
Node 0 is not in any group yet: start a search from it. It reaches 0, 1, 2: group 1, size 3.
-
Step 2 / 3groups2
Node 3 is not in any group yet: start a search from it. It reaches 3, 4: group 2, size 2.
-
Step 3 / 3groups3
Node 5 is not in any group yet: start a search from it. It reaches 5: group 3, size 1. Every node belongs to a group: 3 in all.
How to think about it
The matrix is the adjacency matrix of an undirected graph, and a group is a connected component, so the answer is the number of components. Scan the people in order; each one not yet visited starts a new group, and a search from them marks everyone they can reach.
With an adjacency matrix, listing a person's friends means reading a whole row, so the search costs O(n²) in total. That is optimal here, since the input itself has n² entries.
Complexity. Time O(n²) · Space O(n)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def count_groups(is_connected):
n = len(is_connected)
seen = [False] * n
groups = 0
for s in range(n):
if seen[s]:
continue
groups += 1 # a person in no group yet: a new group
seen[s] = True
stack = [s]
while stack:
u = stack.pop()
for v in range(n): # row u lists u's friends
if is_connected[u][v] == 1 and not seen[v]:
seen[v] = True
stack.append(v)
return groups#include <vector>
int solve(const std::vector<std::vector<int>>& isConnected) {
const int n = isConnected.size();
std::vector<bool> seen(n, false);
int groups = 0;
for (int s = 0; s < n; ++s) {
if (seen[s]) continue;
++groups; // a new group
seen[s] = true;
std::vector<int> stack{s};
while (!stack.empty()) {
int u = stack.back();
stack.pop_back();
for (int v = 0; v < n; ++v)
if (isConnected[u][v] == 1 && !seen[v]) { seen[v] = true; stack.push_back(v); }
}
}
return groups;
}import java.util.*;
class Solution {
public int countGroups(int[][] isConnected) {
int n = isConnected.length, groups = 0;
boolean[] seen = new boolean[n];
for (int s = 0; s < n; s++) {
if (seen[s]) continue;
groups++; // a new group
seen[s] = true;
ArrayDeque<Integer> stack = new ArrayDeque<>();
stack.push(s);
while (!stack.isEmpty()) {
int u = stack.pop();
for (int v = 0; v < n; v++)
if (isConnected[u][v] == 1 && !seen[v]) { seen[v] = true; stack.push(v); }
}
}
return groups;
}
}function countGroups(isConnected) {
const n = isConnected.length, seen = new Array(n).fill(false);
let groups = 0;
for (let s = 0; s < n; s++) {
if (seen[s]) continue;
groups++; // a new group
seen[s] = true;
const stack = [s];
while (stack.length) {
const u = stack.pop();
for (let v = 0; v < n; v++) if (isConnected[u][v] === 1 && !seen[v]) { seen[v] = true; stack.push(v); }
}
}
return groups;
}fn solve<const N: usize>(is_connected: &[[i64; N]]) -> i64 {
let mut seen = [false; N];
let mut groups = 0;
for s in 0..N {
if seen[s] {
continue;
}
groups += 1; // a new group
seen[s] = true;
let mut stack = vec![s];
while let Some(u) = stack.pop() {
for v in 0..N {
if is_connected[u][v] == 1 && !seen[v] {
seen[v] = true;
stack.push(v);
}
}
}
}
groups
}import qualified Data.Set as S
solve :: [[Int]] -> Int
solve m = go [0 .. n - 1] S.empty 0
where
n = length m
friends u = [v | (v, 1) <- zip [0 ..] (m !! u)]
go [] _ groups = groups
go (s : rest) seen groups
| S.member s seen = go rest seen groups
| otherwise = go rest (mark [s] (S.insert s seen)) (groups + 1) -- a new group
mark [] seen = seen
mark (u : st) seen =
let new = [v | v <- friends u, not (S.member v seen)]
in mark (new ++ st) (foldr S.insert seen new)
Variants to try
- Friendships arrive one at a time with timestamps: at what moment does everyone first belong to one group?
- Return the size of each group.
- The relation is not symmetric (follows rather than friendships): count strongly connected components.
Next problems
Count the islands in a grid
Every unvisited land cell starts a new island; flood it so it is never counted again. The first graph problem most interviews ask.
Open the problem → Medium15 minThe largest island
Return the number of cells in the largest island of a 0/1 grid: count each component's cells and keep the maximum.
Open the problem → Medium15 minThe cheapest node in each group
Each node has a value. Find the smallest value in every connected group and return their sum: the components template with f = min and g = sum.
Open the problem →Write a solution and run it against the real test table.
Keep going
Count the islands in a grid
Every unvisited land cell starts a new island; flood it so it is never counted again. The first graph problem most interviews ask.
Open the problem → Medium15 minThe largest island
Return the number of cells in the largest island of a 0/1 grid: count each component's cells and keep the maximum.
Open the problem → Medium15 minThe cheapest node in each group
Each node has a value. Find the smallest value in every connected group and return their sum: the components template with f = min and g = sum.
Open the problem → GraphsThe pattern behind it
Grids, networks and dependencies are all graphs. Breadth-first search finds shortest paths, depth-first search and union-find find what is connected, Dijkstra handles weighted edges, and a queue of nodes with no remaining prerequisites gives an order.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.