Open In App

Dynamic Connectivity | Set 1 (Incremental)

Last Updated : 06 Jun, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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

  1. Incremental Connectivity : Edges are only added to the graph.
  2. Decremental Connectivity : Edges are only deleted from the graph.
  3. 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. 

  1. An edge is added to the graph.
  2. 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);


Output

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.

O(alpha(n))



Previous Article
Next Article

Similar Reads

Dynamic Connectivity | Set 2 (DSU with Rollback)
Dynamic connectivity, in general, refers to the storage of the connectivity of the components of a graph, where the edges change between some or all the queries. The basic operations are - Add an edge between nodes a and bRemove the edge between nodes a and b Types of problems using Dynamic Connectivity Problems using dynamic connectivity can be of
17 min read
Island Connectivity for Q queries
Given an integer N denoting the number of disconnected islands from 1 to N. You have to process Q queries of the following types: Type 1 query: 0 u v =&gt; Connect islands u and v Type 2 query: 1 u v =&gt; Print "YES" if islands u and v are connected otherwise print "NO"Note: The connection between the islands follows transitive property. Examples:
10 min read
Graph Connectivity above Threshold value
Given two integers N and K, where N denotes the number of vertices in a Graph. There is an edge between two vertices u and v if there exists some z &gt; K: u % z ==0: v % z ==0. Now you have to answer Q queries of the form [ i, j ] where you have to print whether i and j are connected (i.e. there is some path between i and j ) or not. Examples: Inp
11 min read
Dynamic Disjoint Set Data Structure for large range values
Prerequisites: Disjoint Set Data StructureSetUnordered_Map 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 si
14 min read
Largest Independent Set Problem using Dynamic Programming
Given a Binary Tree, find size of the Largest Independent Set(LIS) in it. A subset of all tree nodes is an independent set if there is no edge between any two nodes of the subset. For example, consider the following binary tree. The largest independent set(LIS) is {10, 40, 60, 70, 80} and size of the LIS is 5. Recommended PracticeLargest Independen
18 min read
Bitmasking and Dynamic Programming | Set 1 (Count ways to assign unique cap to every person)
Consider the below problem statement. There are 100 different types of caps each having a unique id from 1 to 100. Also, there are 'n' persons each having a collection of a variable number of caps. One day all of these persons decide to go to a party wearing a cap but to look unique they decide that none of them will wear the same type of cap. So,
18 min read
Overlapping Subproblems Property in Dynamic Programming | DP-1
Dynamic Programming is an algorithmic paradigm that solves a given complex problem by breaking it into subproblems using recursion and storing the results of subproblems to avoid computing the same results again. Following are the two main properties of a problem that suggests that the given problem can be solved using Dynamic programming. Overlapp
10 min read
Static Data Structure vs Dynamic Data Structure
Data structure is a way of storing and organizing data efficiently such that the required operations on them can be performed be efficient with respect to time as well as memory. Simply, Data Structure are used to reduce complexity (mostly the time complexity) of the code. Data structures can be two types : 1. Static Data Structure 2. Dynamic Data
4 min read
Introduction to Dynamic Programming on Trees
Dynamic Programming(DP) is a technique to solve problems by breaking them down into overlapping sub-problems which follows the optimal substructure. There are various problems using DP like subset sum, knapsack, coin change etc. DP can also be applied on trees to solve some specific problems.Pre-requisite: DFSGiven a tree with N nodes and N-1 edges
10 min read
Print equal sum sets of Array (Partition Problem) using Dynamic Programming
Given an array arr[]. Determine whether it is possible to split the array into two sets such that the sum of elements in both sets is equal. If it is possible, then print both sets. If it is not possible then output -1. Examples : Input : arr = {5, 5, 1, 11} Output : Set 1 = {5, 5, 1}, Set 2 = {11} Sum of both the sets is 11 and equal. Input : arr
13 min read