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.
Move from brute-force thinking to an efficient approach using union-find strategy.
You are given an integer n. There is an undirected graph with n vertices, numbered from 0 to n - 1. You are given a 2D integer array edges where edges[i] = [ai, bi] denotes that there exists an undirected edge connecting vertices ai and bi.
Return the number of complete connected components of the 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.
A connected component is said to be complete if there exists an edge between every pair of its vertices.
Example 1:
Input: n = 6, edges = [[0,1],[0,2],[1,2],[3,4]] Output: 3 Explanation: From the picture above, one can see that all of the components of this graph are complete.
Example 2:
Input: n = 6, edges = [[0,1],[0,2],[1,2],[3,4],[3,5]] Output: 1 Explanation: The component containing vertices 0, 1, and 2 is complete since there is an edge between every pair of two vertices. On the other hand, the component containing vertices 3, 4, and 5 is not complete since there is no edge between vertices 4 and 5. Thus, the number of complete components in this graph is 1.
Constraints:
1 <= n <= 500 <= edges.length <= n * (n - 1) / 2edges[i].length == 20 <= ai, bi <= n - 1ai != biProblem summary: You are given an integer n. There is an undirected graph with n vertices, numbered from 0 to n - 1. You are given a 2D integer array edges where edges[i] = [ai, bi] denotes that there exists an undirected edge connecting vertices ai and bi. Return the number of complete connected components of the 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. A connected component is said to be complete if there exists an edge between every pair of its vertices.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Union-Find
6 [[0,1],[0,2],[1,2],[3,4]]
6 [[0,1],[0,2],[1,2],[3,4],[3,5]]
number-of-connected-components-in-an-undirected-graph)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2685: Count the Number of Complete Components
class Solution {
private List<Integer>[] g;
private boolean[] vis;
public int countCompleteComponents(int n, int[][] edges) {
g = new List[n];
vis = new boolean[n];
Arrays.setAll(g, k -> new ArrayList<>());
for (int[] e : edges) {
int a = e[0], b = e[1];
g[a].add(b);
g[b].add(a);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
if (!vis[i]) {
int[] t = dfs(i);
if (t[0] * (t[0] - 1) == t[1]) {
++ans;
}
}
}
return ans;
}
private int[] dfs(int i) {
vis[i] = true;
int x = 1, y = g[i].size();
for (int j : g[i]) {
if (!vis[j]) {
int[] t = dfs(j);
x += t[0];
y += t[1];
}
}
return new int[] {x, y};
}
}
// Accepted solution for LeetCode #2685: Count the Number of Complete Components
func countCompleteComponents(n int, edges [][]int) (ans int) {
g := make([][]int, n)
vis := make([]bool, n)
for _, e := range edges {
a, b := e[0], e[1]
g[a] = append(g[a], b)
g[b] = append(g[b], a)
}
var dfs func(int) (int, int)
dfs = func(i int) (int, int) {
vis[i] = true
x, y := 1, len(g[i])
for _, j := range g[i] {
if !vis[j] {
a, b := dfs(j)
x += a
y += b
}
}
return x, y
}
for i := range vis {
if !vis[i] {
a, b := dfs(i)
if a*(a-1) == b {
ans++
}
}
}
return
}
# Accepted solution for LeetCode #2685: Count the Number of Complete Components
class Solution:
def countCompleteComponents(self, n: int, edges: List[List[int]]) -> int:
def dfs(i: int) -> (int, int):
vis[i] = True
x, y = 1, len(g[i])
for j in g[i]:
if not vis[j]:
a, b = dfs(j)
x += a
y += b
return x, y
g = defaultdict(list)
for a, b in edges:
g[a].append(b)
g[b].append(a)
vis = [False] * n
ans = 0
for i in range(n):
if not vis[i]:
a, b = dfs(i)
ans += a * (a - 1) == b
return ans
// Accepted solution for LeetCode #2685: Count the Number of Complete Components
// 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 #2685: Count the Number of Complete Components
// class Solution {
// private List<Integer>[] g;
// private boolean[] vis;
//
// public int countCompleteComponents(int n, int[][] edges) {
// g = new List[n];
// vis = new boolean[n];
// Arrays.setAll(g, k -> new ArrayList<>());
// for (int[] e : edges) {
// int a = e[0], b = e[1];
// g[a].add(b);
// g[b].add(a);
// }
// int ans = 0;
// for (int i = 0; i < n; ++i) {
// if (!vis[i]) {
// int[] t = dfs(i);
// if (t[0] * (t[0] - 1) == t[1]) {
// ++ans;
// }
// }
// }
// return ans;
// }
//
// private int[] dfs(int i) {
// vis[i] = true;
// int x = 1, y = g[i].size();
// for (int j : g[i]) {
// if (!vis[j]) {
// int[] t = dfs(j);
// x += t[0];
// y += t[1];
// }
// }
// return new int[] {x, y};
// }
// }
// Accepted solution for LeetCode #2685: Count the Number of Complete Components
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #2685: Count the Number of Complete Components
// class Solution {
// private List<Integer>[] g;
// private boolean[] vis;
//
// public int countCompleteComponents(int n, int[][] edges) {
// g = new List[n];
// vis = new boolean[n];
// Arrays.setAll(g, k -> new ArrayList<>());
// for (int[] e : edges) {
// int a = e[0], b = e[1];
// g[a].add(b);
// g[b].add(a);
// }
// int ans = 0;
// for (int i = 0; i < n; ++i) {
// if (!vis[i]) {
// int[] t = dfs(i);
// if (t[0] * (t[0] - 1) == t[1]) {
// ++ans;
// }
// }
// }
// return ans;
// }
//
// private int[] dfs(int i) {
// vis[i] = true;
// int x = 1, y = g[i].size();
// for (int j : g[i]) {
// if (!vis[j]) {
// int[] t = dfs(j);
// x += t[0];
// y += t[1];
// }
// }
// return new int[] {x, y};
// }
// }
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.