Number of Unique Categories
MediumUpdated: Aug 2, 2025
Practice on:
Problem
You are given an integer n and an object categoryHandler of class
CategoryHandler.
There are n elements, numbered from 0 to n - 1. Each element has a category, and your task is to find the number of unique categories.
The class CategoryHandler contains the following function, which may help you:
boolean haveSameCategory(integer a, integer b): Returnstrueifaandbare in the same category andfalseotherwise. Also, if eitheraorbis not a valid number (i.e. it's greater than or equal tonor less than0), it returnsfalse.
Return the number of unique categories.
Examples
Example 1:
Input: n = 6, categoryHandler = [1,1,2,2,3,3]
Output: 3
Explanation: There are 6 elements in this example. The first two elements belong to category 1, the second two belong to category 2, and the last two elements belong to category 3. So there are 3 unique categories.
Example 2:
Input: n = 5, categoryHandler = [1,2,3,4,5]
Output: 5
Explanation: There are 5 elements in this example. Each element belongs to a unique category. So there are 5 unique categories.
Example 3:
Input: n = 3, categoryHandler = [1,1,1]
Output: 1
Explanation: There are 3 elements in this example. All of them belong to one category. So there is only 1 unique category.
Constraints:
1 <= n <= 100
Solution
Method 1 – Union-Find (Disjoint Set Union)
Intuition
We can use union-find to group elements that are in the same category. For each pair (i, j), if haveSameCategory(i, j) is true, we union them. The number of unique categories is the number of unique roots.
Approach
- Initialize a parent array for union-find.
- For each i < j, if haveSameCategory(i, j), union i and j.
- Count the number of unique roots at the end.
Code
Java
class Solution {
public int numberOfCategories(int n, CategoryHandler categoryHandler) {
int[] parent = 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 (categoryHandler.haveSameCategory(i, j)) union(parent, i, j);
}
}
Set<Integer> roots = new HashSet<>();
for (int i = 0; i < n; i++) roots.add(find(parent, i));
return roots.size();
}
private int find(int[] parent, int x) {
if (parent[x] != x) parent[x] = find(parent, parent[x]);
return parent[x];
}
private void union(int[] parent, int x, int y) {
parent[find(parent, x)] = find(parent, y);
}
}
Python
class Solution:
def numberOfCategories(self, n: int, categoryHandler: 'CategoryHandler') -> int:
parent = list(range(n))
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(x, y):
parent[find(x)] = find(y)
for i in range(n):
for j in range(i+1, n):
if categoryHandler.haveSameCategory(i, j):
union(i, j)
return len(set(find(i) for i in range(n)))
C++
class Solution {
public:
int numberOfCategories(int n, CategoryHandler& categoryHandler) {
vector<int> parent(n);
iota(parent.begin(), parent.end(), 0);
function<int(int)> find = [&](int x) {
return parent[x] == x ? x : parent[x] = find(parent[x]);
};
auto union_set = [&](int x, int y) {
parent[find(x)] = find(y);
};
for (int i = 0; i < n; ++i) {
for (int j = i+1; j < n; ++j) {
if (categoryHandler.haveSameCategory(i, j)) union_set(i, j);
}
}
unordered_set<int> roots;
for (int i = 0; i < n; ++i) roots.insert(find(i));
return roots.size();
}
};
Go
func numberOfCategories(n int, categoryHandler CategoryHandler) int {
parent := make([]int, n)
for i := range parent { parent[i] = i }
var find func(int) int
find = func(x int) int {
if parent[x] != x { parent[x] = find(parent[x]) }
return parent[x]
}
union := func(x, y int) { parent[find(x)] = find(y) }
for i := 0; i < n; i++ {
for j := i+1; j < n; j++ {
if categoryHandler.HaveSameCategory(i, j) { union(i, j) }
}
}
roots := map[int]struct{}{}
for i := 0; i < n; i++ { roots[find(i)] = struct{}{} }
return len(roots)
}
Kotlin
class Solution {
fun numberOfCategories(n: Int, categoryHandler: CategoryHandler): Int {
val parent = IntArray(n) { it }
fun find(x: Int): Int {
if (parent[x] != x) parent[x] = find(parent[x])
return parent[x]
}
fun union(x: Int, y: Int) { parent[find(x)] = find(y) }
for (i in 0 until n) {
for (j in i+1 until n) {
if (categoryHandler.haveSameCategory(i, j)) union(i, j)
}
}
return (0 until n).map { find(it) }.toSet().size
}
}
Rust
impl Solution {
pub fn number_of_categories(n: i32, category_handler: &mut CategoryHandler) -> i32 {
let n = n as usize;
let mut parent: Vec<usize> = (0..n).collect();
fn find(parent: &mut Vec<usize>, x: usize) -> usize {
if parent[x] != x { parent[x] = find(parent, parent[x]); }
parent[x]
}
for i in 0..n {
for j in i+1..n {
if category_handler.have_same_category(i as i32, j as i32) {
let (a, b) = (find(&mut parent, i), find(&mut parent, j));
parent[a] = b;
}
}
}
let mut roots = std::collections::HashSet::new();
for i in 0..n { roots.insert(find(&mut parent, i)); }
roots.len() as i32
}
}
TypeScript
class Solution {
numberOfCategories(n: number, categoryHandler: { haveSameCategory(a: number, b: number): boolean }): number {
const parent = Array.from({length: n}, (_, i) => i)
const find = (x: number): number => parent[x] === x ? x : (parent[x] = find(parent[x]))
const union = (x: number, y: number) => { parent[find(x)] = find(y) }
for (let i = 0; i < n; i++) {
for (let j = i+1; j < n; j++) {
if (categoryHandler.haveSameCategory(i, j)) union(i, j)
}
}
const roots = new Set<number>()
for (let i = 0; i < n; i++) roots.add(find(i))
return roots.size
}
}
Complexity
- ⏰ Time complexity:
O(n^2 * α(n))(α is inverse Ackermann, nearly constant) - 🧺 Space complexity:
O(n)