Dynamic connectivity is a data structure that dynamically maintains the information about the connected components of graph. In simple words suppose there is a graph G(V, E) in which no. of vertices V is constant but no. of edges E is variable. There are three ways in which we can change the number of edges
- Incremental Connectivity : Edges are only added to the graph.
- Decremental Connectivity : Edges are only deleted from the graph.
- Fully Dynamic Connectivity : Edges can both be deleted and added to the graph.
In this article only Incremental connectivity is discussed. There are mainly two operations that need to be handled.Â
- An edge is added to the graph.
- Information about two nodes x and y whether they are in the same connected components or not.
Example:Â
Input : V = 7 Number of operations = 11 1 0 1 2 0 1 2 1 2 1 0 2 2 0 2 2 2 3 2 3 4 1 0 5 2 4 5 2 5 6 1 2 6 Note: 7 represents number of nodes, 11 represents number of queries. There are two types of queries Type 1: 1 x y in this if the node x and y are connected print Yes else No Type 2: 2 x y in this add an edge between node x and y Output: No Yes No Yes Explanation : Initially there are no edges so node 0 and 1 will be disconnected so answer will be No Node 0 and 2 will be connected through node 1 so answer will be Yes similarly for other queries we can find whether two nodes are connected or not
To solve the problems of incremental connectivity disjoint data structure is used. Here each connected component represents a set and if the two nodes belong to the same set it means that they are connected.Â
Implementation is given below here we are using union by rank and path compressionÂ
C++
// C++ implementation of incremental connectivity #include<bits/stdc++.h> using namespace std; Â
// Finding the root of node i int root( int arr[], int i) { Â Â Â Â while (arr[i] != i) Â Â Â Â { Â Â Â Â Â Â Â arr[i] = arr[arr[i]]; Â Â Â Â Â Â Â i = arr[i]; Â Â Â Â } Â Â Â Â return i; } Â
// union of two nodes a and b void weighted_union( int arr[], int rank[], Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â int a, int b) { Â Â Â Â int root_a = root (arr, a); Â Â Â Â int root_b = root (arr, b); Â
    // union based on rank     if (rank[root_a] < rank[root_b])     {        arr[root_a] = arr[root_b];        rank[root_b] += rank[root_a];     }     else     {         arr[root_b] = arr[root_a];         rank[root_a] += rank[root_b];     } } Â
// Returns true if two nodes have same root bool areSame( int arr[], int a, int b) { Â Â Â Â return (root(arr, a) == root(arr, b)); } Â
// Performing an operation according to query type void query( int type, int x, int y, int arr[], int rank[]) {     // type 1 query means checking if node x and y     // are connected or not     if (type == 1)     {         // If roots of x and y is same then yes         // is the answer         if (areSame(arr, x, y) == true )             cout << "Yes" << endl;         else            cout << "No" << endl;     } Â
    // type 2 query refers union of x and y     else if (type == 2)     {         // If x and y have different roots then         // union them         if (areSame(arr, x, y) == false )             weighted_union(arr, rank, x, y);     } } Â
// Driver function int main() {     // No.of nodes     int n = 7; Â
    // The following two arrays are used to     // implement disjoint set data structure.     // arr[] holds the parent nodes while rank     // array holds the rank of subset     int arr[n], rank[n]; Â
    // initializing both array and rank     for ( int i=0; i<n; i++)     {         arr[i] = i;         rank[i] = 1;     } Â
    // number of queries     int q = 11;     query(1, 0, 1, arr, rank);     query(2, 0, 1, arr, rank);     query(2, 1, 2, arr, rank);     query(1, 0, 2, arr, rank);     query(2, 0, 2, arr, rank);     query(2, 2, 3, arr, rank);     query(2, 3, 4, arr, rank);     query(1, 0, 5, arr, rank);     query(2, 4, 5, arr, rank);     query(2, 5, 6, arr, rank);     query(1, 2, 6, arr, rank);     return 0; } |
Java
// Java implementation of // incremental connectivity import java.util.*; Â
class GFG { Â
// Finding the root of node i static int root( int arr[], int i) { Â Â Â Â while (arr[i] != i) Â Â Â Â { Â Â Â Â Â Â Â Â arr[i] = arr[arr[i]]; Â Â Â Â Â Â Â Â i = arr[i]; Â Â Â Â } Â Â Â Â return i; } Â
// union of two nodes a and b static void weighted_union( int arr[], int rank[], Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â int a, int b) { Â Â Â Â int root_a = root (arr, a); Â Â Â Â int root_b = root (arr, b); Â
    // union based on rank     if (rank[root_a] < rank[root_b])     {         arr[root_a] = arr[root_b];         rank[root_b] += rank[root_a];     }     else     {         arr[root_b] = arr[root_a];         rank[root_a] += rank[root_b];     } } Â
// Returns true if two nodes have same root static boolean areSame( int arr[], Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â int a, int b) { Â Â Â Â return (root(arr, a) == root(arr, b)); } Â
// Performing an operation // according to query type static void query( int type, int x, int y,                   int arr[], int rank[]) {     // type 1 query means checking if     // node x and y are connected or not     if (type == 1 )     {         // If roots of x and y is same then yes         // is the answer         if (areSame(arr, x, y) == true )             System.out.println( "Yes" );         else             System.out.println( "No" );     } Â
    // type 2 query refers union of x and y     else if (type == 2 )     {         // If x and y have different roots then         // union them         if (areSame(arr, x, y) == false )             weighted_union(arr, rank, x, y);     } } Â
// Driver Code public static void main(String[] args) {     // No.of nodes     int n = 7 ; Â
    // The following two arrays are used to     // implement disjoint set data structure.     // arr[] holds the parent nodes while rank     // array holds the rank of subset     int []arr = new int [n];     int []rank = new int [n]; Â
    // initializing both array and rank     for ( int i = 0 ; i < n; i++)     {         arr[i] = i;         rank[i] = 1 ;     } Â
    // number of queries     int q = 11 ;     query( 1 , 0 , 1 , arr, rank);     query( 2 , 0 , 1 , arr, rank);     query( 2 , 1 , 2 , arr, rank);     query( 1 , 0 , 2 , arr, rank);     query( 2 , 0 , 2 , arr, rank);     query( 2 , 2 , 3 , arr, rank);     query( 2 , 3 , 4 , arr, rank);     query( 1 , 0 , 5 , arr, rank);     query( 2 , 4 , 5 , arr, rank);     query( 2 , 5 , 6 , arr, rank);     query( 1 , 2 , 6 , arr, rank); } } Â
// This code is contributed by Rajput-Ji |
Python3
# Python3 implementation of # incremental connectivity Â
# Finding the root of node i def root(arr, i): Â Â Â Â while (arr[i] ! = i): Â Â Â Â Â Â Â Â arr[i] = arr[arr[i]] Â Â Â Â Â Â Â Â i = arr[i] Â Â Â Â return i Â
# union of two nodes a and b def weighted_union(arr, rank, a, b): Â Â Â Â root_a = root (arr, a) Â Â Â Â root_b = root (arr, b) Â
    # union based on rank     if (rank[root_a] < rank[root_b]):         arr[root_a] = arr[root_b]         rank[root_b] + = rank[root_a]     else :         arr[root_b] = arr[root_a]         rank[root_a] + = rank[root_b] Â
# Returns true if two nodes have # same root def areSame(arr, a, b): Â Â Â Â return (root(arr, a) = = root(arr, b)) Â
# Performing an operation according # to query type def query( type , x, y, arr, rank):          # type 1 query means checking if     # node x and y are connected or not     if ( type = = 1 ):                  # If roots of x and y is same         # then yes is the answer         if (areSame(arr, x, y) = = True ):             print ( "Yes" )         else :             print ( "No" ) Â
    # type 2 query refers union of     # x and y     elif ( type = = 2 ):                  # If x and y have different         # roots then union them         if (areSame(arr, x, y) = = False ):             weighted_union(arr, rank, x, y) Â
# Driver Code if __name__ = = '__main__' : Â
    # No.of nodes     n = 7 Â
    # The following two arrays are used to     # implement disjoint set data structure.     # arr[] holds the parent nodes while rank     # array holds the rank of subset     arr = [ None ] * n     rank = [ None ] * n Â
    # initializing both array     # and rank     for i in range (n):         arr[i] = i         rank[i] = 1 Â
    # number of queries     q = 11     query( 1 , 0 , 1 , arr, rank)     query( 2 , 0 , 1 , arr, rank)     query( 2 , 1 , 2 , arr, rank)     query( 1 , 0 , 2 , arr, rank)     query( 2 , 0 , 2 , arr, rank)     query( 2 , 2 , 3 , arr, rank)     query( 2 , 3 , 4 , arr, rank)     query( 1 , 0 , 5 , arr, rank)     query( 2 , 4 , 5 , arr, rank)     query( 2 , 5 , 6 , arr, rank)     query( 1 , 2 , 6 , arr, rank) Â
# This code is contributed by PranchalK |
C#
// C# implementation of // incremental connectivity using System; Â Â Â Â Â class GFG { Â
// Finding the root of node i static int root( int []arr, int i) { Â Â Â Â while (arr[i] != i) Â Â Â Â { Â Â Â Â Â Â Â Â arr[i] = arr[arr[i]]; Â Â Â Â Â Â Â Â i = arr[i]; Â Â Â Â } Â Â Â Â return i; } Â
// union of two nodes a and b static void weighted_union( int []arr, int []rank, Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â int a, int b) { Â Â Â Â int root_a = root (arr, a); Â Â Â Â int root_b = root (arr, b); Â
    // union based on rank     if (rank[root_a] < rank[root_b])     {         arr[root_a] = arr[root_b];         rank[root_b] += rank[root_a];     }     else     {         arr[root_b] = arr[root_a];         rank[root_a] += rank[root_b];     } } Â
// Returns true if two nodes have same root static Boolean areSame( int []arr, Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â int a, int b) { Â Â Â Â return (root(arr, a) == root(arr, b)); } Â
// Performing an operation // according to query type static void query( int type, int x, int y,                   int []arr, int []rank) {     // type 1 query means checking if     // node x and y are connected or not     if (type == 1)     {         // If roots of x and y is same then yes         // is the answer         if (areSame(arr, x, y) == true )             Console.WriteLine( "Yes" );         else             Console.WriteLine( "No" );     } Â
    // type 2 query refers union of x and y     else if (type == 2)     {                  // If x and y have different roots then         // union them         if (areSame(arr, x, y) == false )             weighted_union(arr, rank, x, y);     } } Â
// Driver Code public static void Main(String[] args) {     // No.of nodes     int n = 7; Â
    // The following two arrays are used to     // implement disjoint set data structure.     // arr[] holds the parent nodes while rank     // array holds the rank of subset     int []arr = new int [n];     int []rank = new int [n]; Â
    // initializing both array and rank     for ( int i = 0; i < n; i++)     {         arr[i] = i;         rank[i] = 1;     } Â
    // number of queries     query(1, 0, 1, arr, rank);     query(2, 0, 1, arr, rank);     query(2, 1, 2, arr, rank);     query(1, 0, 2, arr, rank);     query(2, 0, 2, arr, rank);     query(2, 2, 3, arr, rank);     query(2, 3, 4, arr, rank);     query(1, 0, 5, arr, rank);     query(2, 4, 5, arr, rank);     query(2, 5, 6, arr, rank);     query(1, 2, 6, arr, rank); } } Â
// This code is contributed by PrinciRaj1992 |
Javascript
//Javascript implementation of incremental connectivity Â
      // Finding the root of node i       function root(arr, i) {         while (arr[i] != i) {           arr[i] = arr[arr[i]];           i = arr[i];         }         return i;       } Â
      // union of two nodes a and b       function weighted_union(arr, rank, a, b) {         let root_a = root(arr, a);         let root_b = root(arr, b); Â
        // union based on rank         if (rank[root_a] < rank[root_b]) {           arr[root_a] = arr[root_b];           rank[root_b] += rank[root_a];         } else {           arr[root_b] = arr[root_a];           rank[root_a] += rank[root_b];         }       } Â
      // Returns true if two nodes have same root       function areSame(arr, a, b) {         return root(arr, a) == root(arr, b);       } Â
      // Performing an operation according to query type       function query(type, x, y, arr, rank) {         // type 1 query means checking if node x and y         // are connected or not         if (type == 1) {           // If roots of x and y is same then yes           // is the answer           if (areSame(arr, x, y) == true ) console.log( "Yes" );           else console.log( "No" );         } Â
        // type 2 query refers union of x and y         else if (type == 2) {           // If x and y have different roots then           // union them           if (areSame(arr, x, y) == false ) weighted_union(arr, rank, x, y);         }       } Â
      // Driver function Â
      // No.of nodes       let n = 7; Â
      // The following two arrays are used to       // implement disjoint set data structure.       // arr holds the parent nodes while rank       // array holds the rank of subset Â
      let arr = new Array(n);       let rank = new Array(n);       // initializing both array and rank       for (let i = 0; i < n; i++) {         arr[i] = i;         rank[i] = 1;       } Â
      // number of queries       let q = 11;       query(1, 0, 1, arr, rank);       query(2, 0, 1, arr, rank);       query(2, 1, 2, arr, rank);       query(1, 0, 2, arr, rank);       query(2, 0, 2, arr, rank);       query(2, 2, 3, arr, rank);       query(2, 3, 4, arr, rank);       query(1, 0, 5, arr, rank);       query(2, 4, 5, arr, rank);       query(2, 5, 6, arr, rank);       query(1, 2, 6, arr, rank); |
No Yes No Yes
Time Complexity:
The amortized time complexity is O(alpha(n)) per operation where alpha is inverse ackermann function which is nearly constant.
The space complexity is O(n) since we are creating two arrays: arr and rank with length n.
This article is contributed by Ayush Jha. If you like neveropen and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the neveropen main page and help other Geeks. O(alpha(n))
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!