# Time: O(E · 𝛼(n)) ; 𝛼(n) is inverse Ackermann function
# Space: O(V)


class Solution:
    def countComponents(self, n: int, edges: List[List[int]]) -> int:
        parent = [i for i in range(n)]

        # Or like sizes of the components
        rank = [1] * n
        
        def find(node):
            # Parent of node could be itself (i.e, no parent)
            current_node = node
            
            while current_node != parent[current_node]:
                # Path compression to speed up union-find
                parent[current_node] = parent[parent[current_node]]
                current_node = parent[current_node]
            
            return current_node
                
        
        def union(a, b):
            parent_a, parent_b = find(a), find(b)
            
            if parent_a == parent_b:
                return 0
            
            if rank[parent_a] > rank[parent_b]:
                parent[parent_a] = parent_b
                rank[parent_b] += rank[parent_a]
            else:
                parent[parent_b] = parent_a
                rank[parent_a] += rank[parent_b]
            
            return 1
                
        
        result = n
        for a, b in edges:
            result -= union(a, b)
        
        return result