Count the Number of Complete Components
MEDIUMDescription
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 != bi- There are no repeated edges.
Approaches
Checkout 3 different approaches to solve Count the Number of Complete Components. Click on different approaches to view the approach and algorithm in detail.
Brute-Force Check on Components using Adjacency Matrix
This approach first identifies the connected components of the graph using a standard traversal algorithm like Depth-First Search (DFS). For each component found, it then performs a brute-force check to see if it's complete. An adjacency matrix is used to represent the graph, which simplifies checking for an edge between two vertices but requires significant space.
Algorithm
- Graph Representation: Create an
n x nadjacency matrixadj. For each edge[u, v]in the input, setadj[u][v]andadj[v][u]totrue. - Component Identification: Use a
visitedarray to keep track of processed vertices. Iterate fromi = 0ton-1. Ifiis not visited, it marks the beginning of a new connected component. - Traversal: Start a Depth-First Search (DFS) or Breadth-First Search (BFS) from
i. During the traversal, collect all vertices of the component into a list and mark them as visited. - Completeness Check: Once a component is identified, iterate through every pair of distinct vertices
(u, v)within that component. Use the adjacency matrix to check if an edge exists between them (adj[u][v]). If any edge is found to be missing, the component is not complete. - Counting: If all pairs of vertices in a component are connected, increment a counter for complete components.
- Repeat: Continue this process until all vertices in the graph have been visited.
The method involves three main steps:
-
Build Adjacency Matrix: An
n x nmatrixadjis initialized. We iterate through theedgesarray, and for each edge[u, v], we setadj[u][v] = trueandadj[v][u] = true. This providesO(1)lookup for the existence of an edge. -
Find Components and Check Completeness: We iterate through each vertex of the graph. If a vertex hasn't been visited, we start a traversal (like DFS) to find all nodes in its connected component. These nodes are stored in a temporary list. After the traversal for a component is complete, we check if it's a complete graph. This is done by iterating through all unique pairs of nodes in our list and verifying that an edge exists between them using the adjacency matrix.
-
Count: If a component is verified to be complete, we increment a counter. This process is repeated for all components in the graph.
class Solution {
boolean[][] adj;
boolean[] visited;
public int countCompleteComponents(int n, int[][] edges) {
adj = new boolean[n][n];
for (int[] edge : edges) {
adj[edge[0]][edge[1]] = true;
adj[edge[1]][edge[0]] = true;
}
visited = new boolean[n];
int completeComponents = 0;
for (int i = 0; i < n; i++) {
if (!visited[i]) {
List<Integer> component = new ArrayList<>();
dfs(i, component, n);
if (isComplete(component)) {
completeComponents++;
}
}
}
return completeComponents;
}
private void dfs(int u, List<Integer> component, int n) {
visited[u] = true;
component.add(u);
for (int v = 0; v < n; v++) {
if (adj[u][v] && !visited[v]) {
dfs(v, component, n);
}
}
}
private boolean isComplete(List<Integer> component) {
int numNodes = component.size();
for (int i = 0; i < numNodes; i++) {
for (int j = i + 1; j < numNodes; j++) {
int u = component.get(i);
int v = component.get(j);
if (!adj[u][v]) {
return false;
}
}
}
return true;
}
}
Complexity Analysis
Pros and Cons
- The logic is straightforward, especially the completeness check with an adjacency matrix.
- Inefficient in both time and space, with complexities of
O(n^2). - Particularly slow for sparse graphs where the number of edges
Eis much less thann^2.
Code Solutions
Checking out 3 solutions in different languages for Count the Number of Complete Components. Click on different languages to view the code.
Video Solution
Watch the video walkthrough for Count the Number of Complete Components
Similar Questions
5 related questions you might find useful
Algorithms:
Data Structures:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.