Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You are given an array of integers nums of size n and a positive integer threshold.
There is a graph consisting of n nodes with the ith node having a value of nums[i]. Two nodes i and j in the graph are connected via an undirected edge if lcm(nums[i], nums[j]) <= threshold.
Return the number of connected components in this graph.
A connected component is a subgraph of a graph in which there exists a path between any two vertices, and no vertex of the subgraph shares an edge with a vertex outside of the subgraph.
The term lcm(a, b) denotes the least common multiple of a and b.
Example 1:
Input: nums = [2,4,8,3,9], threshold = 5
Output: 4
Explanation:
The four connected components are (2, 4), (3), (8), (9).
Example 2:
Input: nums = [2,4,8,3,9,12], threshold = 10
Output: 2
Explanation:
The two connected components are (2, 3, 4, 8, 9), and (12).
Constraints:
1 <= nums.length <= 1051 <= nums[i] <= 109nums are unique.1 <= threshold <= 2 * 105Problem summary: You are given an array of integers nums of size n and a positive integer threshold. There is a graph consisting of n nodes with the ith node having a value of nums[i]. Two nodes i and j in the graph are connected via an undirected edge if lcm(nums[i], nums[j]) <= threshold. Return the number of connected components in this graph. A connected component is a subgraph of a graph in which there exists a path between any two vertices, and no vertex of the subgraph shares an edge with a vertex outside of the subgraph. The term lcm(a, b) denotes the least common multiple of a and b.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Math · Union-Find
[2,4,8,3,9] 5
[2,4,8,3,9,12] 10
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3378: Count Connected Components in LCM Graph
class DSU {
private Map<Integer, Integer> parent;
private Map<Integer, Integer> rank;
public DSU(int n) {
parent = new HashMap<>();
rank = new HashMap<>();
for (int i = 0; i <= n; i++) {
parent.put(i, i);
rank.put(i, 0);
}
}
public void makeSet(int v) {
parent.put(v, v);
rank.put(v, 1);
}
public int find(int x) {
if (parent.get(x) != x) {
parent.put(x, find(parent.get(x)));
}
return parent.get(x);
}
public void unionSet(int u, int v) {
u = find(u);
v = find(v);
if (u != v) {
if (rank.get(u) < rank.get(v)) {
int temp = u;
u = v;
v = temp;
}
parent.put(v, u);
if (rank.get(u).equals(rank.get(v))) {
rank.put(u, rank.get(u) + 1);
}
}
}
}
class Solution {
public int countComponents(int[] nums, int threshold) {
DSU dsu = new DSU(threshold);
for (int num : nums) {
for (int j = num; j <= threshold; j += num) {
dsu.unionSet(num, j);
}
}
Set<Integer> uniqueParents = new HashSet<>();
for (int num : nums) {
if (num > threshold) {
uniqueParents.add(num);
} else {
uniqueParents.add(dsu.find(num));
}
}
return uniqueParents.size();
}
}
// Accepted solution for LeetCode #3378: Count Connected Components in LCM Graph
type DSU struct {
parent map[int]int
rank map[int]int
}
func NewDSU(n int) *DSU {
dsu := &DSU{
parent: make(map[int]int),
rank: make(map[int]int),
}
for i := 0; i <= n; i++ {
dsu.parent[i] = i
dsu.rank[i] = 0
}
return dsu
}
func (dsu *DSU) Find(x int) int {
if dsu.parent[x] != x {
dsu.parent[x] = dsu.Find(dsu.parent[x])
}
return dsu.parent[x]
}
func (dsu *DSU) Union(u, v int) {
uRoot := dsu.Find(u)
vRoot := dsu.Find(v)
if uRoot != vRoot {
if dsu.rank[uRoot] < dsu.rank[vRoot] {
uRoot, vRoot = vRoot, uRoot
}
dsu.parent[vRoot] = uRoot
if dsu.rank[uRoot] == dsu.rank[vRoot] {
dsu.rank[uRoot]++
}
}
}
func countComponents(nums []int, threshold int) int {
dsu := NewDSU(threshold)
for _, num := range nums {
for j := num; j <= threshold; j += num {
dsu.Union(num, j)
}
}
uniqueParents := make(map[int]struct{})
for _, num := range nums {
if num > threshold {
uniqueParents[num] = struct{}{}
} else {
uniqueParents[dsu.Find(num)] = struct{}{}
}
}
return len(uniqueParents)
}
# Accepted solution for LeetCode #3378: Count Connected Components in LCM Graph
class DSU:
def __init__(self, n):
self.parent = {i: i for i in range(n)}
self.rank = {i: 0 for i in range(n)}
def make_set(self, v):
self.parent[v] = v
self.rank[v] = 1
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union_set(self, u, v):
u = self.find(u)
v = self.find(v)
if u != v:
if self.rank[u] < self.rank[v]:
u, v = v, u
self.parent[v] = u
if self.rank[u] == self.rank[v]:
self.rank[u] += 1
class Solution:
def countComponents(self, nums, threshold):
dsu = DSU(threshold + 1)
for num in nums:
for j in range(num, threshold + 1, num):
dsu.union_set(num, j)
unique_parents = set()
for num in nums:
if num > threshold:
unique_parents.add(num)
else:
unique_parents.add(dsu.find(num))
return len(unique_parents)
// Accepted solution for LeetCode #3378: Count Connected Components in LCM Graph
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3378: Count Connected Components in LCM Graph
// class DSU {
// private Map<Integer, Integer> parent;
// private Map<Integer, Integer> rank;
//
// public DSU(int n) {
// parent = new HashMap<>();
// rank = new HashMap<>();
// for (int i = 0; i <= n; i++) {
// parent.put(i, i);
// rank.put(i, 0);
// }
// }
//
// public void makeSet(int v) {
// parent.put(v, v);
// rank.put(v, 1);
// }
//
// public int find(int x) {
// if (parent.get(x) != x) {
// parent.put(x, find(parent.get(x)));
// }
// return parent.get(x);
// }
//
// public void unionSet(int u, int v) {
// u = find(u);
// v = find(v);
// if (u != v) {
// if (rank.get(u) < rank.get(v)) {
// int temp = u;
// u = v;
// v = temp;
// }
// parent.put(v, u);
// if (rank.get(u).equals(rank.get(v))) {
// rank.put(u, rank.get(u) + 1);
// }
// }
// }
// }
//
// class Solution {
// public int countComponents(int[] nums, int threshold) {
// DSU dsu = new DSU(threshold);
//
// for (int num : nums) {
// for (int j = num; j <= threshold; j += num) {
// dsu.unionSet(num, j);
// }
// }
//
// Set<Integer> uniqueParents = new HashSet<>();
// for (int num : nums) {
// if (num > threshold) {
// uniqueParents.add(num);
// } else {
// uniqueParents.add(dsu.find(num));
// }
// }
//
// return uniqueParents.size();
// }
// }
// Accepted solution for LeetCode #3378: Count Connected Components in LCM Graph
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #3378: Count Connected Components in LCM Graph
// class DSU {
// private Map<Integer, Integer> parent;
// private Map<Integer, Integer> rank;
//
// public DSU(int n) {
// parent = new HashMap<>();
// rank = new HashMap<>();
// for (int i = 0; i <= n; i++) {
// parent.put(i, i);
// rank.put(i, 0);
// }
// }
//
// public void makeSet(int v) {
// parent.put(v, v);
// rank.put(v, 1);
// }
//
// public int find(int x) {
// if (parent.get(x) != x) {
// parent.put(x, find(parent.get(x)));
// }
// return parent.get(x);
// }
//
// public void unionSet(int u, int v) {
// u = find(u);
// v = find(v);
// if (u != v) {
// if (rank.get(u) < rank.get(v)) {
// int temp = u;
// u = v;
// v = temp;
// }
// parent.put(v, u);
// if (rank.get(u).equals(rank.get(v))) {
// rank.put(u, rank.get(u) + 1);
// }
// }
// }
// }
//
// class Solution {
// public int countComponents(int[] nums, int threshold) {
// DSU dsu = new DSU(threshold);
//
// for (int num : nums) {
// for (int j = num; j <= threshold; j += num) {
// dsu.unionSet(num, j);
// }
// }
//
// Set<Integer> uniqueParents = new HashSet<>();
// for (int num : nums) {
// if (num > threshold) {
// uniqueParents.add(num);
// } else {
// uniqueParents.add(dsu.find(num));
// }
// }
//
// return uniqueParents.size();
// }
// }
Use this to step through a reusable interview workflow for this problem.
Track components with a list or adjacency matrix. Each union operation may need to update all n elements’ component labels, giving O(n) per union. For n union operations total: O(n²). Find is O(1) with direct lookup, but union dominates.
With path compression and union by rank, each find/union operation takes O(α(n)) amortized time, where α is the inverse Ackermann function — effectively constant. Space is O(n) for the parent and rank arrays. For m operations on n elements: O(m × α(n)) total.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Temporary multiplications exceed integer bounds.
Usually fails on: Large inputs wrap around unexpectedly.
Fix: Use wider types, modular arithmetic, or rearranged operations.