Dynamic Disjoint Set Data Structure for large range values
Prerequisites:
Disjoint Set data structure is used to keep track of a set of elements partitioned into a number of disjoint (non-overlapping) subsets.
In this article, we will learn about constructing the same Data Structure dynamically. This data structure basically helps in situations where we cannot simply use arrays for creating disjoint sets because of large inputs of order 109.
To illustrate this, consider the following problem. We need to find the total number of connected components in the Graph when the total Number of Vertices can be up to 10^9.
Examples:
Input : Edges : { { 1, 2 },
{ 2, 3 },
{ 4, 5 } }
Output : 2
Explanation: {1, 2, 3} forms a component and
{4, 5} forms another component.
The idea to solve this problem is, we will maintain two hash tables (implemented using unordered_maps in C++). One for parent and other for degree. Parent[V] will give the parent of the component which the Vertex V is part of and Degree will give the number of vertices in that component.
Initially, both Parent and Degree will be empty. We will keep inserting vertices to the maps as sequentially.
See the code and the explanation simultaneously for a better understanding. Below are the methods used in the code to solve the above problem:
- getParent(V): This method will give the parent of the vertex V. Here we recursively find the parent of the vertex V( see code), meanwhile we assign all the vertex in that component to have the same parent.( In a disjoint set data structure all the vertex in the same component have the same parent.)
- Union(): When we add an edge and the two vertexes are of different components we call the Union() method to join both components. Here the parent of the component formed after joining both components will be the parent of the component among the two which had more vertexes before the union. The degree of the new component is updated accordingly.
- getTotalComponent(): Vertex in the same component will have the same parent.
We use unordered_set (STL) to count the total number of components. As we have maintained the Data Structure as Dynamic, there can be any vertex that has not been added to any of the components hence they are different components alone. So the total number of components will be given by,
Total no of Component = Total Vertices - Number of Vertices
in parent (Map) + Number of Component
formed from the Vertexes inserted
in the graph.
Below is the implementation of the above idea:
C++
#include <bits/stdc++.h>
using namespace std;
int N;
int Edges[3][2];
struct DynamicDisjointSetDS {
unordered_map< int , int > parent, degree;
int N;
DynamicDisjointSetDS( int n)
{
N = n;
}
int getParent( int vertex)
{
if (parent.find(vertex) != parent.end()) {
if (parent[vertex] != vertex) {
parent[vertex] =
getParent(parent[vertex]);
return parent[vertex];
}
}
else {
parent.insert(make_pair(vertex, vertex));
degree.insert(make_pair(vertex, 1));
}
return vertex;
}
void Union( int vertexA, int vertexB)
{
int x = getParent(vertexA);
int y = getParent(vertexB);
if (x == y)
return ;
if (degree[x] > degree[y]) {
parent[y] = x;
degree[x] = degree[x] + degree[y];
}
else {
parent[x] = y;
degree[y] = degree[y] + degree[x];
}
}
int GetTotalComponent()
{
unordered_set< int > total;
for ( auto itr = parent.begin();
itr != parent.end(); itr++) {
total.insert(getParent(itr->first));
}
return N - parent.size() + total.size();
}
};
void Solve()
{
DynamicDisjointSetDS dsu(N);
for ( int i = 0; i < 3; i++) {
if (dsu.getParent(Edges[i][0]) ==
dsu.getParent(Edges[i][1])) {
continue ;
}
else {
dsu.Union(Edges[i][0], Edges[i][1]);
}
}
cout << dsu.GetTotalComponent();
}
int main()
{
N = 5;
Edges[0][0] = 1;
Edges[0][1] = 2;
Edges[1][0] = 2;
Edges[1][1] = 3;
Edges[2][0] = 4;
Edges[2][1] = 3;
Solve();
return 0;
}
|
Java
import java.util.*;
class DynamicDisjointSetDS {
Map<Integer, Integer> parent, degree;
int N;
DynamicDisjointSetDS( int n)
{
N = n;
parent = new HashMap<>();
degree = new HashMap<>();
}
int getParent( int vertex)
{
if (parent.containsKey(vertex)) {
if (parent.get(vertex) != vertex) {
parent.put(vertex,
getParent(parent.get(vertex)));
return parent.get(vertex);
}
}
else {
parent.put(vertex, vertex);
degree.put(vertex, 1 );
}
return vertex;
}
void union( int vertexA, int vertexB)
{
int x = getParent(vertexA);
int y = getParent(vertexB);
if (x == y) {
return ;
}
if (degree.get(x) > degree.get(y)) {
parent.put(y, x);
degree.put(x, degree.get(x) + degree.get(y));
}
else {
parent.put(x, y);
degree.put(y, degree.get(y) + degree.get(x));
}
}
int getTotalComponent()
{
Set<Integer> total = new HashSet<>();
for (Map.Entry<Integer, Integer> entry :
parent.entrySet()) {
total.add(getParent(entry.getKey()));
}
return N - parent.size() + total.size();
}
}
public class Main {
public static void main(String[] args)
{
int N = 5 ;
int [][] Edges = { { 1 , 2 }, { 2 , 3 }, { 4 , 3 } };
DynamicDisjointSetDS dsu
= new DynamicDisjointSetDS(N);
for ( int i = 0 ; i < 3 ; i++) {
if (dsu.getParent(Edges[i][ 0 ])
== dsu.getParent(Edges[i][ 1 ])) {
continue ;
}
else {
dsu.union(Edges[i][ 0 ], Edges[i][ 1 ]);
}
}
System.out.println(dsu.getTotalComponent());
}
}
|
Python3
class DynamicDisjointSetDS:
def __init__( self , n):
self .N = n
self .parent = {}
self .degree = {}
def getParent( self , vertex):
if vertex in self .parent:
if self .parent[vertex] ! = vertex:
self .parent[vertex] = \
self .getParent( self .parent[vertex])
return self .parent[vertex]
else :
self .parent[vertex] = vertex
self .degree[vertex] = 1
return vertex
def Union( self , vertexA, vertexB):
x = self .getParent(vertexA)
y = self .getParent(vertexB)
if x = = y:
return
if self .degree[x] > self .degree[y]:
self .parent[y] = x
self .degree[x] = ( self .degree[x] +
self .degree[y])
else :
self .parent[x] = y
self .degree[y] = ( self .degree[y] +
self .degree[x])
def GetTotalComponent( self ):
total = set ()
for itr in self .parent:
total.add( self .getParent(itr))
return self .N - len ( self .parent) + len (total)
def Solve(N):
dsu = DynamicDisjointSetDS(N)
for i in range ( 0 , 3 ):
if (dsu.getParent(Edges[i][ 0 ]) = =
dsu.getParent(Edges[i][ 1 ])):
continue
else :
dsu.Union(Edges[i][ 0 ], Edges[i][ 1 ])
print (dsu.GetTotalComponent())
if __name__ = = "__main__" :
N = 5
Edges = [[ 1 , 2 ], [ 2 , 3 ], [ 4 , 3 ]]
Solve(N)
|
C#
using System;
using System.Collections.Generic;
public class GFG {
static int N;
static int [, ] Edges = new int [3, 2];
public class DynamicDisjointSetDS {
Dictionary< int , int > parent
= new Dictionary< int , int >();
Dictionary< int , int > degree
= new Dictionary< int , int >();
int N;
public DynamicDisjointSetDS( int n) { N = n; }
public int GetParent( int vertex)
{
if (parent.ContainsKey(vertex)) {
if (parent[vertex] != vertex) {
parent[vertex]
= GetParent(parent[vertex]);
return parent[vertex];
}
}
else {
parent[vertex] = vertex;
degree[vertex] = 1;
}
return vertex;
}
public void Union( int vertexA, int vertexB)
{
int x = GetParent(vertexA);
int y = GetParent(vertexB);
if (x == y)
return ;
if (degree[x] > degree[y]) {
parent[y] = x;
degree[x] += degree[y];
}
else {
parent[x] = y;
degree[y] += degree[x];
}
}
public int GetTotalComponent()
{
HashSet< int > total = new HashSet< int >();
foreach ( int key in new List< int >(parent.Keys))
{
total.Add(GetParent(key));
}
return N - parent.Count + total.Count;
}
}
public static void Solve()
{
DynamicDisjointSetDS dsu
= new DynamicDisjointSetDS(N);
for ( int i = 0; i < 3; i++) {
if (dsu.GetParent(Edges[i, 0])
== dsu.GetParent(Edges[i, 1])) {
continue ;
}
else {
dsu.Union(Edges[i, 0], Edges[i, 1]);
}
}
Console.WriteLine(dsu.GetTotalComponent());
}
public static void Main()
{
N = 5;
Edges[0, 0] = 1;
Edges[0, 1] = 2;
Edges[1, 0] = 2;
Edges[1, 1] = 3;
Edges[2, 0] = 4;
Edges[2, 1] = 3;
Solve();
}
}
|
Javascript
const N = 5;
const Edges = [[1, 2], [2, 3], [4, 5]];
class DynamicDisjointSetDS {
constructor(n) {
this .N = n;
this .parent = new Map();
this .degree = new Map();
}
getParent(vertex) {
if ( this .parent.has(vertex)) {
if ( this .parent.get(vertex) !== vertex) {
this .parent.set(vertex, this .getParent( this .parent.get(vertex)));
return this .parent.get(vertex);
}
}
else {
this .parent.set(vertex, vertex);
this .degree.set(vertex, 1);
}
return vertex;
}
Union(vertexA, vertexB) {
const x = this .getParent(vertexA);
const y = this .getParent(vertexB);
if (x === y) {
return ;
}
if ( this .degree.get(x) > this .degree.get(y)) {
this .parent.set(y, x);
this .degree.set(x, this .degree.get(x) + this .degree.get(y));
} else {
this .parent.set(x, y);
this .degree.set(y, this .degree.get(y) + this .degree.get(x));
}
}
GetTotalComponent() {
const total = new Set();
for (const [key, value] of this .parent) {
total.add( this .getParent(key));
}
return this .N - this .parent.size + total.size;
}
}
function solve() {
const dsu = new DynamicDisjointSetDS(N);
for (let i = 0; i < Edges.length; i++) {
if (dsu.getParent(Edges[i][0]) === dsu.getParent(Edges[i][1])) {
continue ;
}
else {
dsu.Union(Edges[i][0], Edges[i][1]);
}
}
console.log(dsu.GetTotalComponent());
}
solve();
|
Output:
2
Time Complexity: O(E * alpha(N)), where E is the number of edges in the graph, N is the number of vertices in the graph, and alpha is the inverse Ackermann function, which has a value less than 5 for all practical values of N.
Space Complexity: O(N), where N is the total number of vertices in the graph.
Note: If the number of vertices is even larger, we can implement the same code just by changing the data type from int to long.
Last Updated :
14 Mar, 2023
Like Article
Save Article
Share your thoughts in the comments
Please Login to comment...