Given a graph with n vertices (0 to n-1) and m edges. You can remove one edge from anywhere and add that edge between any two vertices in one operation.
Find the minimum number of operations that will be required to connect the graph. If it is not possible to connect the graph, return -1.
Examples:
Input: n = 4, edges[][] = [[0, 1], [0, 2], [1, 2]]
Output: 1
Explanation: Remove edge between vertices 1 and 2 and add between vertices 1 and 3.
Input: n = 6, edges[][] = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3]]Output: 2
Explanation: Remove edge between (1, 2) and(0, 3) and add edge between (1, 4) and (3, 5)
Table of Content
Count Components using DFS - O(n + m) Time and O(n + m) Space
To connect all n vertices, at least n - 1 edges are required. Therefore, if the graph has fewer than n - 1 edges, it is impossible to connect all vertices. Otherwise, we count the number of connected components in the graph using DFS. If there are c connected components, then we need exactly c - 1 operations to connect them, because each operation can connect two components and reduce the number of components by one.
Lets discuss with an example:

Since the graph has at least n - 1 = 6 - 1 = 5 edges, there are enough edges to connect all disconnected components. Thus, the problem reduces to counting the number of connected components. If there are 3 components, the minimum number of operations required is 3 - 1 = 2.
Steps:
- If m < n - 1, return -1.
- Build the adjacency list of the graph.
- Use DFS to find the number of connected components.
- If the number of components is c, return c - 1.
#include <iostream>
#include<vector>
using namespace std;
// DFS to visit all vertices in the current component
void dfs(int node, vector<vector<int>>& adj, vector<bool>& vis) {
vis[node] = true;
for (int nei : adj[node]) {
if (!vis[nei]) {
dfs(nei, adj, vis);
}
}
}
int minEdgesReq(int n, vector<vector<int>>& edges) {
// A connected graph with n vertices requires
// at least (n - 1) edges
if ((int)edges.size() < n - 1) {
return -1;
}
// Build adjacency list
vector<vector<int>> adj(n);
for (auto& edge : edges) {
int u = edge[0];
int v = edge[1];
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<bool> vis(n, false);
int components = 0;
// Count connected components using DFS.
for (int i = 0; i < n; i++) {
if (!vis[i]) {
components++;
dfs(i, adj, vis);
}
}
// To connect 'components' disconnected parts,
// we need exactly (components - 1) operations
return components - 1;
}
int main() {
int n = 4;
vector<vector<int>> edges = {
{0, 1},
{0, 2},
{1, 2}
};
cout << minEdgesReq(n, edges) << "\n";
return 0;
}
import java.util.ArrayList;
import java.util.List;
public class GFG {
// DFS to visit all vertices in the current component
private static void dfs(int node, List<List<Integer>> adj, boolean[] vis) {
vis[node] = true;
for (int nei : adj.get(node)) {
if (!vis[nei]) {
dfs(nei, adj, vis);
}
}
}
public static int minEdgesReq(int n, int[][] edges) {
// A connected graph with n vertices requires
// at least (n - 1) edges
if (edges.length < n - 1) {
return -1;
}
// Build adjacency list.
List<List<Integer>> adj = new ArrayList<>();
for (int i = 0; i < n; i++) {
adj.add(new ArrayList<>());n }
for (int[] edge : edges) {
int u = edge[0];
int v = edge[1];
adj.get(u).add(v);
adj.get(v).add(u);
}
boolean[] vis = new boolean[n];
int components = 0;
// Count connected components using DFS
for (int i = 0; i < n; i++) {
if (!vis[i]) {
components++;
dfs(i, adj, vis);
}
}
// To connect 'components' disconnected parts,
// we need exactly (components - 1) operations
return components - 1;
}
public static void main(String[] args) {
int n = 4;
int[][] edges = {
{0, 1},
{0, 2},
{1, 2}
};
System.out.println(minEdgesReq(n, edges));
}
}
def dfs(node, adj, vis):
# DFS to visit all vertices in the current component
vis[node] = True
for nei in adj[node]:
if not vis[nei]:
dfs(nei, adj, vis)
def minEdgesReq(n, edges):
# A connected graph with n vertices requires
# at least (n - 1) edges
if len(edges) < n - 1:
return -1
# Build adjacency list
adj = [[] for _ in range(n)]
for edge in edges:
u = edge[0]
v = edge[1]
adj[u].append(v)
adj[v].append(u)
vis = [False] * n
components = 0
# Count connected components using DFS
for i in range(n):
if not vis[i]:
components += 1
dfs(i, adj, vis)
# To connect 'components' disconnected parts,
# we need exactly (components - 1) operations
return components - 1
if __name__ == "__main__":
n = 4
edges = [
[0, 1],
[0, 2],
[1, 2]
]
print(minEdgesReq(n, edges))
using System;
using System.Collections.Generic;
public class GFG
{
// DFS to visit all vertices in the current component
private static void Dfs(int node, List<List<int>> adj, bool[] vis)
{
vis[node] = true;
foreach (int nei in adj[node])
{
if (!vis[nei])
{
Dfs(nei, adj, vis);
}
}
}
public static int minEdgesReq(int n, int[][] edges)
{
// A connected graph with n vertices requires
// at least (n - 1) edges
if (edges.Length < n - 1)
{
return -1;
}
// Build adjacency list
List<List<int>> adj = new List<List<int>>();
for (int i = 0; i < n; i++)
{
adj.Add(new List<int>());
}
foreach (int[] edge in edges)
{
int u = edge[0];
int v = edge[1];
adj[u].Add(v);
adj[v].Add(u);
}
bool[] vis = new bool[n];
int components = 0;
// Count connected components using DFS
for (int i = 0; i < n; i++)
{
if (!vis[i])
{
components++;
Dfs(i, adj, vis);
}
}
// To connect 'components' disconnected parts,
// we need exactly (components - 1) operations
return components - 1;
}
public static void Main()
{
int n = 4;
int[][] edges = {
new int[] { 0, 1 },
new int[] { 0, 2 },
new int[] { 1, 2 }
};
Console.WriteLine(minEdgesReq(n, edges));
}
}
function dfs(node, adj, vis) {
// DFS to visit all vertices in the current component
vis[node] = true;
for (let nei of adj[node]) {
if (!vis[nei]) {
dfs(nei, adj, vis);
}
}
}
function minEdgesReq(n, edges) {
// A connected graph with n vertices requires
// at least (n - 1) edges
if (edges.length < n - 1) {
return -1;
}
// Build adjacency list
let adj = Array.from({length: n}, () => []);
for (let edge of edges) {
let u = edge[0];
let v = edge[1];
adj[u].push(v);
adj[v].push(u);
}
let vis = Array(n).fill(false);
let components = 0;
// Count connected components using DFS
for (let i = 0; i < n; i++) {
if (!vis[i]) {
components++;
dfs(i, adj, vis);
}
}
// To connect 'components' disconnected parts,
// we need exactly (components - 1) operations
return components - 1;
}
// Driver code
let n = 4;
let edges = [
[0, 1],
[0, 2],
[1, 2]
];
console.log(minEdgesReq(n, edges));
Output
1
Count Components using DSU - O(n + m) Time and O(n) Space
Similar to the DFS approach, a graph needs at least n - 1 edges to become connected. If there are enough edges, we can use Disjoint Set Union (DSU) to efficiently determine how many connected components exist.
Initially, every vertex forms its own component. For each edge, we merge the components of its endpoints. After processing all edges, if there are c connected components, then c - 1 operations are required to connect the entire graph.
Steps:
- If m < n - 1, return -1.
- Initialize DSU with each vertex as its own parent.
- Process every edge and union the sets containing its endpoints.
- Count the number of distinct components.
- If the number of components is c, return c - 1.
#include <iostream>
#include<vector>
using namespace std;
// Find the representative (root) of a set
// Path compression flattens the tree, making future finds faster
int find(int x, vector<int>& parent) {
if (parent[x] != x) {
parent[x] = find(parent[x], parent);
}
return parent[x];
}
int minEdgesReq(int n, vector<vector<int>>& edges) {
// At least (n - 1) edges are needed to connect n nodes
if ((int)edges.size() < n - 1) {
return -1;
}
// Initially every node is its own parent
vector<int> parent(n);
for (int i = 0; i < n; i++)
parent[i] = i;
// Size of each component
vector<int> size(n, 1);
for (auto& e : edges) {
int ru = find(e[0], parent);
int rv = find(e[1], parent);
// Already in the same component
if (ru == rv)
continue;
// Attach the smaller component to the larger one
if (size[ru] < size[rv])
swap(ru, rv);
parent[rv] = ru;
size[ru] += size[rv];
}
int components = 0;
// Count the number of connected components
for (int i = 0; i < n; i++) {
if (find(i, parent) == i)
components++;
}
// Need (components - 1) edges to connect all components
return components - 1;
}
int main() {
int n = 4;
vector<vector<int>> edges = {
{0, 1},
{0, 2},
{1, 2}
};
cout << minEdgesReq(n, edges) << "\n";
return 0;
}
import java.util.Arrays;
public class GFG {
// Find the representative (root) of a set
// Path compression flattens the tree, making future finds faster
public static int find(int x, int[] parent) {
if (parent[x]!= x) {
parent[x] = find(parent[x], parent);
}
return parent[x];
}
public static int minEdgesReq(int n, int[][] edges) {
// At least (n - 1) edges are needed to connect n nodes
if (edges.length < n - 1) {
return -1;
}
// Initially every node is its own parent
int[] parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
// Size of each component
int[] size = new int[n];
Arrays.fill(size, 1);
for (int[] e : edges) {
int ru = find(e[0], parent);
int rv = find(e[1], parent);
// Already in the same component
if (ru == rv)
continue;
// Attach the smaller component to the larger one
if (size[ru] < size[rv])
swap(size, ru, rv);
parent[rv] = ru;
size[ru] += size[rv];
}
int components = 0;
// Count the number of connected components
for (int i = 0; i < n; i++) {
if (find(i, parent) == i)
components++;
}
// Need (components - 1) edges to connect all components
return components - 1;
}
private static void swap(int[] size, int a, int b) {
int temp = size[a];
size[a] = size[b];
size[b] = temp;
}
public static void main(String[] args) {
int n = 4;
int[][] edges = {
{0, 1},
{0, 2},
{1, 2}
};
System.out.println(minEdgesReq(n, edges));
}
}
def find(x, parent):
# Find the representative (root) of a set
# Path compression flattens the tree, making future finds faster
if parent[x]!= x:
parent[x] = find(parent[x], parent)
return parent[x]
def minEdgesReq(n, edges):
# At least (n - 1) edges are needed to connect n nodes
if len(edges) < n - 1:
return -1
# Initially every node is its own parent
parent = [i for i in range(n)]
# Size of each component
size = [1] * n
for e in edges:
ru = find(e[0], parent)
rv = find(e[1], parent)
# Already in the same component
if ru == rv:
continue
# Attach the smaller component to the larger one
if size[ru] < size[rv]:
ru, rv = rv, ru
parent[rv] = ru
size[ru] += size[rv]
components = 0
# Count the number of connected components
for i in range(n):
if find(i, parent) == i:
components += 1
# Need (components - 1) edges to connect all components
return components - 1
if __name__ == "__main__":
n = 4
edges = [
[0, 1],
[0, 2],
[1, 2]
]
print(minEdgesReq(n, edges))
using System;
public class GFG {
// Find the representative (root) of a set
// Path compression flattens the tree, making future finds faster
public static int Find(int x, int[] parent) {
if (parent[x]!= x) {
parent[x] = Find(parent[x], parent);
}
return parent[x];
}
public static int minEdgesReq(int n, int[,] edges) {
// At least (n - 1) edges are needed to connect n nodes
if (edges.GetLength(0) < n - 1) {
return -1;
}
// Initially every node is its own parent
int[] parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
// Size of each component
int[] size = new int[n];
for (int i = 0; i < n; i++) {
size[i] = 1;
}
for (int i = 0; i < edges.GetLength(0); i++) {
int ru = Find(edges[i, 0], parent);
int rv = Find(edges[i, 1], parent);
// Already in the same component
if (ru == rv)
continue;
// Attach the smaller component to the larger one
if (size[ru] < size[rv])
Swap(size, ref ru, ref rv);
parent[rv] = ru;
size[ru] += size[rv];
}
int components = 0;
// Count the number of connected components
for (int i = 0; i < n; i++) {
if (Find(i, parent) == i)
components++;
}
// Need (components - 1) edges to connect all components
return components - 1;
}
private static void Swap(int[] size, ref int a, ref int b) {
int temp = size[a];
size[a] = size[b];
size[b] = temp;
}
public static void Main() {
int n = 4;
int[,] edges = {
{0, 1},
{0, 2},
{1, 2}
};
Console.WriteLine(minEdgesReq(n, edges));
}
}
function find(x, parent) {
// Find the representative (root) of a set
// Path compression flattens the tree, making future finds faster
if (parent[x]!== x) {
parent[x] = find(parent[x], parent);
}
return parent[x];
}
function minEdgesReq(n, edges) {
// At least (n - 1) edges are needed to connect n nodes
if (edges.length < n - 1) {
return -1;
}
// Initially every node is its own parent
let parent = Array.from({length: n}, (_, i) => i);
// Size of each component
let size = Array(n).fill(1);
for (let e of edges) {
let ru = find(e[0], parent);
let rv = find(e[1], parent);
// Already in the same component
if (ru === rv)
continue;
// Attach the smaller component to the larger one
if (size[ru] < size[rv])
[ru, rv] = [rv, ru];
parent[rv] = ru;
size[ru] += size[rv];
}
let components = 0;
// Count the number of connected components
for (let i = 0; i < n; i++) {
if (find(i, parent) === i)
components++;
}
// Need (components - 1) edges to connect all components
return components - 1;
}
// Driver code
let n = 4;
let edges = [
[0, 1],
[0, 2],
[1, 2]
];
console.log(minEdgesReq(n, edges));
Output
1
Output: 
Output: 