Given an array arr[][] which contains the edges of a graph to be used to construct an undirected graph G with N nodes, the task is to find the maximum component size in the graph after each edge is added while constructing the graph.
Examples:
Input: N = 4, arr[][] = {{1, 2}, {3, 4}, {2, 3}}
Output: 2 2 4
Explanation:
Initially, the graph has 4 individual nodes 1, 2, 3 and 4.
After the first edge is added : 1 – 2, 3, 4 -> maximum component size = 2
After the second edge is added : 1 – 2, 3 – 4 -> maximum component size = 2
After the third edge is added : 1 – 2 – 3 – 4 -> maximum component size = 4Input: N = 4, arr[][] = {{2, 3}, {1, 2}, {1, 5}, {2, 4}}
Output: 2 3 4 5
Naive Approach: The naive approach for this problem is to add the edges sequentially and at each step apply depth-first search algorithm to find the size of the largest component.
Below is the implementation of the above approach:
C++
// C++ program to find the // maximum comake_paironent size // after addition of each // edge to the graph #include <bits/stdc++.h> using namespace std; // Function to perform // Depth First Search // on the given graph int dfs( int u, int visited[], vector< int >* adj) { // Mark visited visited[u] = 1; int size = 1; // Add each child's // comake_paironent size for ( auto child : adj[u]) { if (!visited[child]) size += dfs(child, visited, adj); } return size; } // Function to find the maximum // comake_paironent size // after addition of each // edge to the graph void maxSize(vector<pair< int , int > > e, int n) { // Graph in the adjacency // list format vector< int > adj[n]; // Visited array int visited[n]; vector< int > answer; // At each step, add a new // edge and apply dfs on all // the nodes to find the maximum // comake_paironent size for ( auto edge : e) { // Add this edge to undirected graph adj[edge.first - 1].push_back( edge.second - 1); adj[edge.second - 1].push_back( edge.first - 1); // Mark all the nodes // as unvisited memset (visited, 0, sizeof (visited)); int maxAns = 0; // Loop to perform DFS // and find the size // of the maximum comake_paironent for ( int i = 0; i < n; i++) { if (!visited[i]) { maxAns = max(maxAns, dfs(i, visited, adj)); } } answer.push_back(maxAns); } // Print the answer for ( auto i : answer) { cout << i << " " ; } } // Driver code int main() { int N = 4; vector<pair< int , int > > E; E.push_back(make_pair(1, 2)); E.push_back(make_pair(3, 4)); E.push_back(make_pair(2, 3)); maxSize(E, N); return 0; } |
Java
// Java program to find the maximum // comake_paironent size after // addition of each edge to the graph import java.util.*; @SuppressWarnings ( "unchecked" ) class GFG{ static class pair { int Key, Value; pair( int Key, int Value) { this .Key = Key; this .Value = Value; } } // Function to perform Depth First // Search on the given graph static int dfs( int u, int []visited, ArrayList []adj) { // Mark visited visited[u] = 1 ; int size = 1 ; // Add each child's // comake_paironent size for ( int child : (ArrayList<Integer>)adj[u]) { if (visited[child] == 0 ) size += dfs(child, visited, adj); } return size; } // Function to find the maximum // comake_paironent size after // addition of each edge to the graph static void maxSize(ArrayList e, int n) { // Graph in the adjacency // list format ArrayList []adj = new ArrayList[n]; for ( int i = 0 ; i < n; i++) { adj[i] = new ArrayList(); } // Visited array int []visited = new int [n]; ArrayList answer = new ArrayList(); // At each step, add a new // edge and apply dfs on all // the nodes to find the maximum // comake_paironent size for (pair edge : (ArrayList<pair>)e) { // Add this edge to undirected graph adj[edge.Key - 1 ].add( edge.Value - 1 ); adj[edge.Value - 1 ].add( edge.Key - 1 ); // Mark all the nodes // as unvisited Arrays.fill(visited, 0 ); int maxAns = 0 ; // Loop to perform DFS and find the // size of the maximum comake_paironent for ( int i = 0 ; i < n; i++) { if (visited[i] == 0 ) { maxAns = Math.max(maxAns, dfs(i, visited, adj)); } } answer.add(maxAns); } // Print the answer for ( int i : (ArrayList<Integer>) answer) { System.out.print(i + " " ); } } // Driver code public static void main(String[] args) { int N = 4 ; ArrayList E = new ArrayList(); E.add( new pair( 1 , 2 )); E.add( new pair( 3 , 4 )); E.add( new pair( 2 , 3 )); maxSize(E, N); } } // This code is contributed by pratham76 |
Python3
# Python3 program to find the # maximum comake_paironent size # after addition of each # edge to the graph # Function to perform # Depth First Search # on the given graph def dfs(u, visited, adj): # Mark visited visited[u] = 1 size = 1 # Add each child's # comake_paironent size for child in adj[u]: if (visited[child] = = 0 ): size + = dfs(child, visited, adj) return size # Function to find the maximum # comake_paironent size # after addition of each # edge to the graph def maxSize(e, n): # Graph in the adjacency # list format adj = [] for i in range (n): adj.append([]) # Visited array visited = [ 0 ] * (n) answer = [] # At each step, add a new # edge and apply dfs on all # the nodes to find the maximum # comake_paironent size for edge in e: # Add this edge to undirected graph adj[edge[ 0 ] - 1 ].append(edge[ 1 ] - 1 ) adj[edge[ 1 ] - 1 ].append(edge[ 0 ] - 1 ) # Mark all the nodes # as unvisited visited = [ 0 ] * (n) maxAns = 0 # Loop to perform DFS # and find the size # of the maximum comake_paironent for i in range (n): if (visited[i] = = 0 ): maxAns = max (maxAns, dfs(i, visited, adj)) answer.append(maxAns) # Print the answer for i in answer: print (i, " ", end = " ") N = 4 E = [] E.append([ 1 , 2 ]) E.append([ 3 , 4 ]) E.append([ 2 , 3 ]) maxSize(E, N) # This code is contributed by divyesh072019. |
C#
// C# program to find the // maximum comake_paironent size // after addition of each // edge to the graph using System; using System.Collections; using System.Collections.Generic; class GFG{ // Function to perform // Depth First Search // on the given graph static int dfs( int u, int []visited, ArrayList []adj) { // Mark visited visited[u] = 1; int size = 1; // Add each child's // comake_paironent size foreach ( int child in adj[u]) { if (visited[child] == 0) size += dfs(child, visited, adj); } return size; } // Function to find the maximum // comake_paironent size // after addition of each // edge to the graph static void maxSize(ArrayList e, int n) { // Graph in the adjacency // list format ArrayList []adj = new ArrayList[n]; for ( int i = 0; i < n; i++) { adj[i] = new ArrayList(); } // Visited array int []visited = new int [n]; ArrayList answer = new ArrayList(); // At each step, add a new // edge and apply dfs on all // the nodes to find the maximum // comake_paironent size foreach (KeyValuePair< int , int > edge in e) { // Add this edge to undirected graph adj[edge.Key - 1].Add( edge.Value - 1); adj[edge.Value - 1].Add( edge.Key - 1); // Mark all the nodes // as unvisited Array.Fill(visited,0); int maxAns = 0; // Loop to perform DFS // and find the size // of the maximum comake_paironent for ( int i = 0; i < n; i++) { if (visited[i] == 0) { maxAns = Math.Max(maxAns, dfs(i, visited, adj)); } } answer.Add(maxAns); } // Print the answer foreach ( int i in answer) { Console.Write(i + " " ); } } // Driver code public static void Main( string [] args) { int N = 4; ArrayList E = new ArrayList(); E.Add( new KeyValuePair< int , int >(1, 2)); E.Add( new KeyValuePair< int , int >(3, 4)); E.Add( new KeyValuePair< int , int >(2, 3)); maxSize(E, N); } } // This code is contributed by rutvik_56 |
Javascript
<script> // Javascript program to find the // maximum comake_paironent size // after addition of each // edge to the graph // Function to perform // Depth First Search // on the given graph function dfs(u, visited, adj) { // Mark visited visited[u] = 1; let size = 1; // Add each child's // comake_paironent size for (let child = 0; child < adj[u].length; child++) { if (visited[adj[u][child]] == 0) { size += dfs(adj[u][child], visited, adj); } } return size; } // Function to find the maximum // comake_paironent size // after addition of each // edge to the graph function maxSize(e, n) { // Graph in the adjacency // list format let adj = []; for (let i = 0; i < n; i++) { adj.push([]); } // Visited array let visited = new Array(n); visited.fill(0); let answer = []; // At each step, add a new // edge and apply dfs on all // the nodes to find the maximum // comake_paironent size for (let edge = 0; edge < e.length; edge++) { // Add this edge to undirected graph adj[e[edge][0] - 1].push(e[edge][1] - 1); adj[e[edge][1] - 1].push(e[edge][0] - 1); // Mark all the nodes // as unvisited visited.fill(0); let maxAns = 0; // Loop to perform DFS // and find the size // of the maximum comake_paironent for (let i = 0; i < n; i++) { if (visited[i] == 0) maxAns = Math.max(maxAns, dfs(i, visited, adj)); } answer.push(maxAns); } // Print the answer for (let i = 0; i < answer.length; i++) { document.write(answer[i] + " " ); } } let N = 4; let E = []; E.push([1, 2]); E.push([3, 4]); E.push([2, 3]); maxSize(E, N); // This code is contributed by divyeshrabadiya07. </script> |
2 2 4
Time Complexity: O(|E| * N)
Efficient Approach: The idea is to use the concept of Disjoint Set (Union by rank and Path compression) to solve the problem more efficiently.
- Each node is initially a disjoint set within itself. As and when the edges are added, the disjoint sets are merged together forming larger components. In the disjoint set implementation, we will make the ranking system based on component sizes i.e when merging of two components is performed the larger component’s root will be considered the final root after the merge operation.
- One way to find the largest size component after each edge addition is to traverse the size array (size[i] represents the size of the component in which node ‘i’ belongs), but this is inefficient when the number of nodes in the graph is high.
- A more efficient way is to store the component sizes of all the root in some ordered data structure like sets.
- When two components are merged, all we need to do is remove the previous component sizes from the set and add the combined component size. So at each step, we would be able to find the largest component size in logarithmic complexity.
Below is the implementation of the above approach:
C++
// C++ implementation to find the maximum // component size after the addition of // each edge to the graph #include <bits/stdc++.h> using namespace std; // Variables for implementing DSU int par[100005]; int size[100005]; // Root of the component of node i int root( int i) { if (par[i] == i) return i; // Finding the root and applying // path compression else return par[i] = root(par[i]); } // Function to merge two components void merge( int a, int b) { // Find the roots of both // the components int p = root(a); int q = root(b); // If both the nodes already belong // to the same component if (p == q) return ; // Union by rank, the rank in // this case is the size of // the component. // Smaller size will be // merged into larger, // so the larger's root // will be the final root if (size[p] > size[q]) swap(p, q); par[p] = q; size[q] += size[p]; } // Function to find the // maximum component size // after the addition of // each edge to the graph void maxSize(vector<pair< int , int > > e, int n) { // Initialising the disjoint set for ( int i = 1; i < n + 1; i++) { // Each node is the root and // each component size is 1 par[i] = i; size[i] = 1; } vector< int > answer; // A multiset is being used to store // the size of the components // because multiple components // can have same sizes multiset< int > compSizes; for ( int i = 1; i <= n; i++) compSizes.insert(size[i]); // At each step; add a new edge, // merge the components // and find the max // sized component for ( auto edge : e) { // Merge operation is required only when // both the nodes don't belong to the // same component if (root(edge.first) != root(edge.second)) { // Sizes of the components int size1 = size[root(edge.first)]; int size2 = size[root(edge.second)]; // Remove the previous component sizes compSizes.erase(compSizes.find(size1)); compSizes.erase(compSizes.find(size2)); // Perform the merge operation merge(edge.first, edge.second); // Insert the combined size compSizes.insert(size1 + size2); } // Maximum value in the multiset is // the max component size answer.push_back(*compSizes.rbegin()); } // Printing the answer for ( int i = 0; i < answer.size(); i++) { cout << answer[i] << " " ; } } // Driver code int main() { int N = 4; vector<pair< int , int > > E; E.push_back(make_pair(1, 2)); E.push_back(make_pair(3, 4)); E.push_back(make_pair(2, 3)); maxSize(E, N); return 0; } |
Java
/*package whatever //do not write package name here */ import java.io.*; import java.util.*; // java implementation to find the maximum // component size after the addition of // each edge to the graph class MyPair { private final int key; private final int value; public MyPair( int aKey, int aValue) { key = aKey; value = aValue; } public int key() { return key; } public int value() { return value; } } class GFG { // Variables for implementing DSU static int [] par = new int [ 100005 ]; static int [] s = new int [ 100005 ]; // Root of the component of node i static int root( int i) { if (par[i] == i) return i; // Finding the root and applying // path compression else return par[i] = root(par[i]); } // Function to merge two components static void merge( int a, int b) { // Find the roots of both // the components int p = root(a); int q = root(b); // If both the nodes already belong // to the same component if (p == q) return ; // Union by rank, the rank in // this case is the size of // the component. // Smaller size will be // merged into larger, // so the larger's root // will be the final root if (s[p] > s[q]){ int temp = p; p = q; q = temp; } par[p] = q; s[q] += s[p]; } // Function to find the // maximum component size // after the addition of // each edge to the graph static void maxSize(ArrayList<MyPair> e, int n) { // Initialising the disjoint set for ( int i = 1 ; i < n + 1 ; i++) { // Each node is the root and // each component size is 1 par[i] = i; s[i] = 1 ; } ArrayList<Integer> answer = new ArrayList<Integer>(); // A multiset is being used to store // the size of the components // because multiple components // can have same sizes ArrayList<Integer> compSizes = new ArrayList<Integer> (); for ( int i = 1 ; i <= n; i++) compSizes.add(s[i]); // At each step; add a new edge, // merge the components // and find the max // sized component for ( int i = 0 ; i < e.size(); i++){ MyPair edge = e.get(i); // Merge operation is required only when // both the nodes don't belong to the // same component if (root(edge.key()) != root(edge.value())) { // Sizes of the components int size1 = s[root(edge.key())]; int size2 = s[root(edge.value())]; // Remove the previous component sizes compSizes.remove(compSizes.indexOf(size1)); compSizes.remove(compSizes.indexOf(size2)); // Perform the merge operation merge(edge.key(), edge.value()); // Insert the combined size compSizes.add(size1 + size2); Collections.sort(compSizes); } // Maximum value in the multiset is // the max component size answer.add(compSizes.get(compSizes.size() - 1 )); } // Printing the answer for ( int i = 0 ; i < answer.size(); i++) { System.out.print(answer.get(i) + " " ); } } // Driver code public static void main(String[] args) { int N = 4 ; ArrayList<MyPair> E = new ArrayList<MyPair>(); E.add( new MyPair( 1 , 2 )); E.add( new MyPair( 3 , 4 )); E.add( new MyPair( 2 , 3 )); maxSize(E, N); } } // This code is contributed by Arushi Jindal. |
Python3
# Python3 implementation to find the maximum # component size after the addition of # each edge to the graph # Variables for implementing DSU par = [ - 1 ] * 100005 size = [ - 1 ] * 100005 # Root of the component of node i def root(i): if (par[i] = = i): return i # Finding the root and applying # path compression par[i] = root(par[i]) return par[i] # Function to merge two components def merge(a, b): # Find the roots of both # the components p = root(a) q = root(b) # If both the nodes already belong # to the same component if (p = = q): return # Union by rank, the rank in # this case is the size of # the component. # Smaller size will be # merged into larger, # so the larger's root # will be the final root if (size[p] > size[q]): p,q = q,p par[p] = q size[q] + = size[p] # Function to find the # maximum component size # after the addition of # each edge to the graph def maxSize(e, n): # Initialising the disjoint set for i in range ( 1 , n + 1 ): # Each node is the root and # each component size is 1 par[i] = i size[i] = 1 answer = [] # A multiset is being used to store # the size of the components # because multiple components # can have same sizes compSizes = dict () for i in range ( 1 ,n + 1 ): compSizes[size[i]] = compSizes.get(size[i], 0 ) + 1 # At each step add a new edge, # merge the components # and find the max # sized component for edge in e: # Merge operation is required only when # both the nodes don't belong to the # same component if (root(edge[ 0 ]) ! = root(edge[ 1 ])) : # Sizes of the components size1 = size[root(edge[ 0 ])] size2 = size[root(edge[ 1 ])] # Remove the previous component sizes compSizes[size1] - = 1 if compSizes[size1] = = 0 : del compSizes[size1] compSizes[size2] - = 1 if compSizes[size2] = = 0 : del compSizes[size2] # Perform the merge operation merge(edge[ 0 ], edge[ 1 ]) # Insert the combined size compSizes[size1 + size2] = compSizes.get(size1 + size2, 0 ) + 1 # Maximum value in the multiset is # the max component size answer.append( max (compSizes.keys())) # Printing the answer for i in range ( len (answer)) : print (answer[i],end = ' ' ) # Driver code if __name__ = = '__main__' : N = 4 E = [] E.append(( 1 , 2 )) E.append(( 3 , 4 )) E.append(( 2 , 3 )) maxSize(E, N) |
C#
using System; using System.Collections.Generic; class Program { // Variables for implementing DSU static int [] par = new int [100005]; static int [] size = new int [100005]; // Root of the component of node i static int root( int i) { if (par[i] == i) return i; else return par[i] = root(par[i]); } // Function to merge two components static void merge( int a, int b) { int p = root(a); int q = root(b); if (p == q) return ; if (size[p] > size[q]) { int temp = p; p = q; q = temp; } par[p] = q; size[q] += size[p]; } // Function to find the // maximum component size // after the addition of // each edge to the graph static void maxSize(List<Tuple< int , int >> e, int n) { for ( int i = 1; i < n + 1; i++) { par[i] = i; size[i] = 1; } List< int > answer = new List< int >(); SortedSet< int > compSizes = new SortedSet< int >(); for ( int i = 1; i <= n; i++) compSizes.Add(size[i]); foreach ( var edge in e) { if (root(edge.Item1) != root(edge.Item2)) { int size1 = size[root(edge.Item1)]; int size2 = size[root(edge.Item2)]; compSizes.Remove(size1); compSizes.Remove(size2); merge(edge.Item1, edge.Item2); compSizes.Add(size1 + size2); } answer.Add(compSizes.Max); } foreach ( int i in answer) { Console.Write(i + " " ); } } static void Main( string [] args) { int N = 4; List<Tuple< int , int >> E = new List<Tuple< int , int >>(); E.Add(Tuple.Create(1, 2)); E.Add(Tuple.Create(3, 4)); E.Add(Tuple.Create(2, 3)); maxSize(E, N); Console.ReadLine(); } } |
Javascript
// JavaScript implementation to find the maximum // component size after the addition of // each edge to the graph // Variables for implementing DSU const par = []; const size = []; // Root of the component of node i function root(i) { if (par[i] === i) { return i; } // Finding the root and applying // path compression else { return (par[i] = root(par[i])); } } // Function to merge two components function merge(a, b) { // Find the roots of both // the components let p = root(a); let q = root(b); // If both the nodes already belong // to the same component if (p === q) { return ; } // Union by rank, the rank in // this case is the size of // the component. // Smaller size will be // merged into larger, // so the larger's root // will be the final root if (size[p] > size[q]) { [p, q] = [q, p]; } par[p] = q; size[q] += size[p]; } // Function to find the // maximum component size // after the addition of // each edge to the graph function maxSize(e, n) { // Initialising the disjoint set for (let i = 1; i < n + 1; i++) { // Each node is the root and // each component size is 1 par[i] = i; size[i] = 1; } const answer = []; // A set is being used to store // the size of the components // because multiple components // can have same sizes const compSizes = new Set(size.slice(1, n + 1)); // At each step; add a new edge, // merge the components // and find the max // sized component for (const [a, b] of e) { // Merge operation is required only when // both the nodes don't belong to the // same component if (root(a) !== root(b)) { // Sizes of the components const size1 = size[root(a)]; const size2 = size[root(b)]; // Remove the previous component sizes compSizes. delete (size1); compSizes. delete (size2); // Perform the merge operation merge(a, b); // Insert the combined size compSizes.add(size1 + size2); } // Maximum value in the set is // the max component size answer.push(Math.max(...compSizes)); } // Printing the answer console.log(answer.join( " " )); } // Driver code const N = 4; const E = [ [1, 2], [3, 4], [2, 3], ]; maxSize(E, N); |
2 2 4
Time Complexity: O(|E| * log(N))
Auxiliary Space: O(N)
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!