How Big Is Your Friend Group
Solve this ProblemPeople start out alone, and friendships merge groups. You receive a list of operations: "merge the groups of a and b", or "how big is the group of a?". Answer every operation in order with the size of the group of a (after the merge, for merge operations).
A disjoint set (union-find) stores the group sizes at the roots of trees. Union by size hooks the smaller tree under the larger one, which keeps the trees shallow.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ n ≤ 10 people numbered 0 … n-1; at the start every person is alone in their own group - ◆
ops is a list of at most 12 operations, each a row [kind, a, b] with 0 ≤ a, b < n: kind 1 means "merge the groups of a and b" (nothing changes if they are already together), kind 2 means "ask for the size of the group of a" (b is ignored) - ◆
The operations are processed in the given order and every operation produces one answer - ◆
Return the answers: for a question the size of the group of a; for a merge the size of the group of a AFTER the merge
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Keep Group Labels and Count the Members
BruteKeep a group label per person (at first their own number). To merge a and b when their labels differ, sweep over all people and give everyone with b's label the label of a. To report a size, count how many people carry a's label. Both merging and counting sweep all n people, so every operation costs O(n).
O(n) per operationO(n)1class Solution {
2 public int[] groupSizes(int n, int[][] ops) {
3 int[] label = new int[n];
4 for (int i = 0; i < n; i++) label[i] = i;
5 int[] answers = new int[ops.length];
6 for (int k = 0; k < ops.length; k++) {
7 int a = ops[k][1], b = ops[k][2];
8 if (ops[k][0] == 1 && label[a] != label[b]) {
9 int from = label[b], to = label[a];
10 for (int i = 0; i < n; i++) {
11 if (label[i] == from) label[i] = to;
12 }
13 }
14 int size = 0;
15 for (int i = 0; i < n; i++) {
16 if (label[i] == label[a]) size++;
17 }
18 answers[k] = size;
19 }
20 return answers;
21 }
22}Optimal — Disjoint Set With Union by Size and Path Compression
OptimalKeep every group as a tree with parent[] pointers towards the root, and store the number of people in each tree at its root (size[]). find(x) walks up to the root, shortening the path as it goes (path compression). To merge, hook the root of the SMALLER group under the root of the LARGER group and add the sizes; making the big tree the root keeps trees shallow. The size of a person's group is size[find(x)], so each operation takes nearly constant time.
O(α(n)) per operationO(n)1class Solution {
2 private int find(int[] parent, int x) {
3 while (parent[x] != x) {
4 parent[x] = parent[parent[x]];
5 x = parent[x];
6 }
7 return x;
8 }
9
10 public int[] groupSizes(int n, int[][] ops) {
11 int[] parent = new int[n];
12 int[] size = new int[n];
13 for (int i = 0; i < n; i++) {
14 parent[i] = i;
15 size[i] = 1;
16 }
17 int[] answers = new int[ops.length];
18 for (int k = 0; k < ops.length; k++) {
19 int ra = find(parent, ops[k][1]);
20 if (ops[k][0] == 1) {
21 int rb = find(parent, ops[k][2]);
22 if (ra != rb) {
23 if (size[ra] < size[rb]) {
24 int tmp = ra;
25 ra = rb;
26 rb = tmp;
27 }
28 parent[rb] = ra;
29 size[ra] += size[rb];
30 }
31 }
32 answers[k] = size[ra];
33 }
34 return answers;
35 }
36}