Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions problems/2685-count-the-number-of-complete-components/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 2685. Count the Number of Complete Components

[LeetCode Link](https://leetcode.com/problems/count-the-number-of-complete-components/)

Difficulty: Medium
Topics: Depth-First Search, Breadth-First Search, Union-Find, Graph Theory
Acceptance Rate: 78.2%

## Hints

### Hint 1

This problem is about **connected components** in an undirected graph. Whenever you need to group vertices that are reachable from one another, think about the classic tools for the job: a graph traversal (DFS/BFS) or a **Union-Find (Disjoint Set Union)** structure. Start by asking yourself how you would separate the graph into its independent pieces.

### Hint 2

Once you can identify each component, you need a way to decide whether a component is *complete*. A complete graph on `k` vertices has a very specific, well-known number of edges. So for each component, the two quantities you should track are: **how many vertices** it contains and **how many edges** belong entirely inside it.

### Hint 3

The critical insight is the formula for a complete graph: a component with `k` vertices is complete **iff** it contains exactly `k * (k - 1) / 2` edges (every pair of vertices connected exactly once). Because the input has no repeated edges, you can simply count vertices and edges per component and compare against this formula. Equivalently, every vertex in a complete component of size `k` must have degree `k - 1`.

## Approach

We use **Union-Find** to group the vertices into connected components, then verify completeness with a counting argument.

1. **Build the DSU.** Initialize `parent[i] = i` and `size[i] = 1` for every vertex. For each edge `[a, b]`, union the two vertices. Use path compression and union-by-size to keep operations near-constant time. After processing all edges, every component is represented by a single root, and `size[root]` tells us how many vertices are in that component.

2. **Count edges per component.** Iterate over the edges again. Both endpoints of any edge are guaranteed to be in the same component, so take the root of one endpoint and increment an `edgeCount[root]` tally. This gives the exact number of edges inside each component.

3. **Check completeness.** For every vertex that is a root of its component, let `k = size[root]` and `e = edgeCount[root]`. The component is complete exactly when `e == k * (k - 1) / 2`. Count how many roots satisfy this.

Why it works: a connected component with `k` vertices needs at least `k - 1` edges (a spanning tree). It becomes *complete* only when every pair of vertices shares an edge, which is precisely `C(k, 2) = k * (k - 1) / 2` edges. Since the problem guarantees no duplicate edges, an edge count equal to this maximum can only happen when all pairs are present. Isolated vertices form complete components of size 1 (0 edges, and `1 * 0 / 2 = 0`), so they are correctly counted.

**Example walkthrough** (`n = 6, edges = [[0,1],[0,2],[1,2],[3,4],[3,5]]`):
- Component `{0, 1, 2}`: `k = 3`, edges inside = 3. `3 * 2 / 2 = 3` → complete. ✅
- Component `{3, 4, 5}`: `k = 3`, edges inside = 2 (missing 4–5). `3 * 2 / 2 = 3 ≠ 2` → not complete. ❌

Answer: `1`.

## Complexity Analysis

Time Complexity: O(V + E · α(V)), where `V = n` and `E = edges.length`. Each union/find is effectively constant time with path compression and union-by-size (α is the inverse Ackermann function). We make one pass to union edges, one pass to count edges, and one pass over vertices to check roots.

Space Complexity: O(V) for the `parent`, `size`, and `edgeCount` arrays.

## Edge Cases

- **No edges (`edges = []`):** Every vertex is its own component of size 1 with 0 internal edges. Each is trivially complete, so the answer is `n`. The formula `1 * 0 / 2 = 0` handles this naturally.
- **Single vertex (`n = 1`):** One isolated, complete component → answer `1`.
- **Fully connected graph:** All `n` vertices form one component with `n * (n - 1) / 2` edges → answer `1`. Good for confirming the completeness formula.
- **A component that is connected but not complete** (e.g. a path or a tree of ≥ 3 vertices): connected but missing edges, so it must be excluded. This is the case that distinguishes "connected" from "complete."
- **Mix of complete and incomplete components:** Ensures per-component counting is isolated correctly and one incomplete component doesn't affect the others.
61 changes: 61 additions & 0 deletions problems/2685-count-the-number-of-complete-components/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
number: "2685"
frontend_id: "2685"
title: "Count the Number of Complete Components"
slug: "count-the-number-of-complete-components"
difficulty: "Medium"
topics:
- "Depth-First Search"
- "Breadth-First Search"
- "Union-Find"
- "Graph Theory"
acceptance_rate: 7815.1
is_premium: false
created_at: "2026-07-11T03:50:11.512454+00:00"
fetched_at: "2026-07-11T03:50:11.512454+00:00"
link: "https://leetcode.com/problems/count-the-number-of-complete-components/"
date: "2026-07-11"
---

# 2685. Count the Number of Complete Components

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:**

**![](https://assets.leetcode.com/uploads/2023/04/11/screenshot-from-2023-04-11-23-31-23.png)**


**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:**

**![](https://assets.leetcode.com/uploads/2023/04/11/screenshot-from-2023-04-11-23-32-00.png)**


**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.
74 changes: 74 additions & 0 deletions problems/2685-count-the-number-of-complete-components/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

// Approach: Union-Find (Disjoint Set Union).
//
// 1. Union every edge so vertices in the same connected component share a root.
// Track the number of vertices in each component via a size array.
// 2. Count how many edges lie inside each component by tallying edges per root.
// 3. A component with k vertices is "complete" iff it holds exactly
// k*(k-1)/2 edges (every pair connected). Since the input has no repeated
// edges, matching this maximum guarantees completeness.
//
// Time: O(V + E * α(V)) Space: O(V)

type dsu struct {
parent []int
size []int
}

func newDSU(n int) *dsu {
d := &dsu{
parent: make([]int, n),
size: make([]int, n),
}
for i := 0; i < n; i++ {
d.parent[i] = i
d.size[i] = 1
}
return d
}

func (d *dsu) find(x int) int {
for d.parent[x] != x {
d.parent[x] = d.parent[d.parent[x]] // path compression
x = d.parent[x]
}
return x
}

func (d *dsu) union(a, b int) {
ra, rb := d.find(a), d.find(b)
if ra == rb {
return
}
if d.size[ra] < d.size[rb] {
ra, rb = rb, ra
}
d.parent[rb] = ra
d.size[ra] += d.size[rb]
}

func countCompleteComponents(n int, edges [][]int) int {
d := newDSU(n)
for _, e := range edges {
d.union(e[0], e[1])
}

// Count edges belonging to each component root.
edgeCount := make([]int, n)
for _, e := range edges {
edgeCount[d.find(e[0])]++
}

complete := 0
for v := 0; v < n; v++ {
if d.find(v) != v {
continue // only inspect component roots
}
k := d.size[v]
if edgeCount[v] == k*(k-1)/2 {
complete++
}
}
return complete
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import "testing"

func TestSolution(t *testing.T) {
tests := []struct {
name string
n int
edges [][]int
expected int
}{
{
name: "example 1: all components complete",
n: 6,
edges: [][]int{{0, 1}, {0, 2}, {1, 2}, {3, 4}},
expected: 3,
},
{
name: "example 2: one incomplete triangle path",
n: 6,
edges: [][]int{{0, 1}, {0, 2}, {1, 2}, {3, 4}, {3, 5}},
expected: 1,
},
{
name: "edge case: no edges, all isolated vertices",
n: 4,
edges: [][]int{},
expected: 4,
},
{
name: "edge case: single vertex",
n: 1,
edges: [][]int{},
expected: 1,
},
{
name: "edge case: fully connected graph is one complete component",
n: 4,
edges: [][]int{{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}},
expected: 1,
},
{
name: "edge case: single connected but incomplete path",
n: 3,
edges: [][]int{{0, 1}, {1, 2}},
expected: 0,
},
{
name: "edge case: single edge pair is complete",
n: 2,
edges: [][]int{{0, 1}},
expected: 1,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := countCompleteComponents(tt.n, tt.edges)
if result != tt.expected {
t.Errorf("countCompleteComponents(%d, %v) = %d, want %d",
tt.n, tt.edges, result, tt.expected)
}
})
}
}