Problem

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.

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.

Examples

Example 1:

graph TD
    0 --- 1
    0 --- 2
    1 --- 2
    3 --- 4
    5
  
1
2
3
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:

graph TD
    0 --- 1
    0 --- 2
    1 --- 2
    3 --- 4
    3 --- 5
  
1
2
3
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 <= 50
  • 0 <= edges.length <= n * (n - 1) / 2
  • edges[i].length == 2
  • 0 <= ai, bi <= n - 1
  • ai != bi
  • There are no repeated edges.

Solution

First, lets understand connected components:

  • connected component means all vertices in a subgraph are reachable from each other.
  • complete connected component means that every pair of vertices in the connected component has an edge directly connecting them. This implies the graph forms a clique (dense subset where all vertices are directly connected).

Possible solutions:

  • We’ll use Depth First Search (DFS) or BFS to identify connected components in the graph.
  • For each connected component, check if the number of edges equals (vertices_count * (vertices_count - 1)) / 2, which is the number of edges required to connect all pairs.

Method 1 - Using DFS

Here is the algorithm:

  • Build the graph using adjacency lists for efficient traversal.
  • Traverse the graph using DFS or BFS to find all connected components.
  • Count the number of edges and vertices in each connected component.
  • For each connected component, check if it satisfies the condition to be complete.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Solution {
    public int countCompleteComponents(int n, int[][] edges) {
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            graph.add(new ArrayList<>());
        }
        for (int[] e : edges) {
            graph.get(e[0]).add(e[1]);
            graph.get(e[1]).add(e[0]);
        }
        
        boolean[] visited = new boolean[n];
        int ans = 0;
        
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                int[] res = dfs(i, graph, visited);
                int vertCount = res[0];
                int edgeCount = res[1] / 2; // Each edge is counted twice
                if (edgeCount == vertCount * (vertCount - 1) / 2) {
                    ans++;
                }
            }
        }
        return ans;
    }
    
    private int[] dfs(int node, List<List<Integer>> graph, boolean[] visited) {
        Stack<Integer> stack = new Stack<>();
        stack.push(node);
        visited[node] = true;
        int vertCount = 0;
        int edgeCount = 0;
        
        while (!stack.isEmpty()) {
            int cur = stack.pop();
            vertCount++;
            for (int neigh : graph.get(cur)) {
                edgeCount++;
                if (!visited[neigh]) {
                    visited[neigh] = true;
                    stack.push(neigh);
                }
            }
        }
        return new int[]{vertCount, edgeCount};
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution:
    def countCompleteComponents(self, n: int, edges: List[List[int]]) -> int:
        from collections import defaultdict, deque
        
        graph = defaultdict(list)
        for a, b in edges:
            graph[a].append(b)
            graph[b].append(a)
        
        visited = [False] * n
        ans = 0
        
        def dfs(node: int) -> (int, int):
            stack = [node]
            visited[node] = True
            nodes = 0
            edg_count = 0
            
            while stack:
                cur = stack.pop()
                nodes += 1
                for neigh in graph[cur]:
                    edg_count += 1
                    if not visited[neigh]:
                        visited[neigh] = True
                        stack.append(neigh)
            
            return nodes, edg_count // 2  # Each edge is counted twice
        
        for i in range(n):
            if not visited[i]:
                vert_count, edge_count = dfs(i)
                if edge_count == (vert_count * (vert_count - 1)) // 2:
                    ans += 1
        
        return ans

Complexity

  • ⏰ Time complexity: O(V + E) to traverse the graph (V is the number of vertices, E is the number of edges). Validating edges for connected components takes O(E) extra in worst-case (already scanned edges).
  • 🧺 Space complexity: O(V + E) for adjacency list and visited array.