The Problem
You have a graph of n
nodes. You are given an integer n
and an array edges
where edges[i] = [ai, bi]
indicates that there is an edge between ai
and bi
in the graph.
Return the number of connected components in the graph.
Examples
Input: n = 5, edges = [[0,1],[1,2],[3,4]]
Output: 2
Input: n = 5, edges = [[0,1],[1,2],[2,3],[3,4]]
Output: 1
Constraints
- 1 <= n <= 2000
- 1 <= edges.length <= 5000
- edges[i].length == 2
- 0 <= ai, bi < n
- ai != bi
- There are no repeated edges.
Depth-First Search Solution
func countComponents(_ n: Int, _ edges: [[Int]]) -> Int {
var adj: [Int: [Int]] = [:]
for i in 0 ..< n {
adj[i] = []
}
var visit = Array(repeating: false, count: n)
for value in edges {
let u = value[0]
let v = value[1]
adj[u]!.append(v)
adj[v]!.append(u)
}
func dfs(_ node: Int) {
for nei in adj[node]! {
if !visit[nei] {
visit[nei] = true
dfs(nei)
}
}
}
var res = 0
for node in 0 ..< n {
if !visit[node] {
visit[node] = true
dfs(node)
res += 1
}
}
return res
}
Explanation
From the description, we learn that we need to find the number of connected components in a graph. Let’s clarify what a connected component means:
- If we are given two nodes, for example,
3
and4
, without edges, then we just return the number of nodes given. In this case, we have two nodes, so we return2
because:- One node by itself counts as a connected component.
To solve this problem, we are going to:
- Create an adjacency list.
- Traverse every single node using the DFS algorithm.
- Mark nodes as visited.
- Count the number of connected components by the number of DFS calls.
Time/Space Complexity
- Time complexity: O(V + E)
- Space complexity: O(V + E)
- Where
V
is the number of vertices andE
is the number of edges.
Union-Find Solution
final class DSU {
private var parent: [Int]
private var rank: [Int]
init(_ n: Int) {
self.parent = Array(0 ..< n)
self.rank = Array(repeating: 1, count: n)
}
func find(_ node: Int) -> Int {
var cur = node
while cur != self.parent[cur] {
self.parent[cur] = self.parent[self.parent[cur]]
cur = self.parent[cur]
}
return cur
}
func union(_ u: Int, _ v: Int) -> Bool {
var pu = self.find(u)
var pv = self.find(v)
if pu == pv {
return false
}
if self.rank[pv] > self.rank[pu] {
let tmp = pu
pu = pv
pv = tmp
}
self.parent[pv] = pu
self.rank[pu] += self.rank[pv]
return true
}
}
func countComponents(_ n: Int, _ edges: [[Int]]) -> Int {
let dsu = DSU(n)
var res = n
for value in edges {
let u = value[0]
let v = value[1]
if dsu.union(u, v) {
res -= 1
}
}
return res
}
Explanation
We can also solve this problem using the Union-Find algorithm. This algorithm was literally designed for problems like this—it helps find connected components and identify disjoint sets.
We maintain two arrays:
parent
, where each node initially points to itself. For example,[0,1,2,3,4]
means each node is its own parent.rank
, which helps track the size of each connected component.
Union-Find works like a forest of trees. Initially, we have n
trees (one for each node). As we traverse through each edge, we:
- Connect the nodes (merge them).
- Decrease the number of connected components by
1
.
We add one small optimization by maintaining the rank
, which helps keep track of the size of connected components. As we continue connecting nodes, we always attach a smaller tree to the root of a larger tree to minimize tree height.
For example, if we connect [1,2]
, we make 0
the root and add 2
as a child of 0
. This means the rank
of 0
increases to 3
, and the parent
array changes from [0,1,2,3,4]
to [0,0,0,3,4]
.
Once we process edges [3,4]
, the rank
of 3
increases to 2
, and the parent array updates to [0,0,0,3,3]
. Each time we perform a union operation, we decrement the number of connected components by 1
.
If we encounter an edge like [0,2]
, we immediately return before making changes because they already share the same parent.
Time/Space Complexity
- Time complexity: O(V + (E * α(V)))
- Space complexity: O(V)
- Where
V
is the number of vertices,E
is the number of edges, andα(V)
is the inverse Ackermann function (which is nearly constant in practical use).