Second Best Minimum Spanning Tree

Last Updated : 23 Nov, 2025

Given a weighted undirected graph represented using an adjacency list adj[][], where each adj[u] contains pairs of the form {v, w}, indicating that vertex u is connected to vertex v with an edge weight w. Find the distance of the second best Minimum Spanning Tree (MST) of this graph.

A second best MST is defined as the minimum-weight spanning tree whose total weight is strictly greater than the weight of the minimum spanning tree, but as small as possible. If no such spanning tree exists, return -1.

Examples:

Input: adj[][] = [[[1, 4], [2, 3]],
[[0, 4], [2, 1], [3, 5]],
[[0, 3], [1, 1], [3, 7], [4, 10]],
[[1, 5], [2, 7], [4, 2]],
[[2, 10], [3, 2]]]
Output: 12
Explanation:

420046988
Try It Yourself
redirect icon

[Approach] Using Kruskal For Every Edge

The idea is that we will first run Kruskal’s algorithm to build the MST and record all its edges. Now, to search for the second best MST, we try removing each MST edge one by one. For every removed edge, we run Kruskal again on the remaining edges, which forces the algorithm to pick a different edge to reconnect the graph. This produces a new spanning tree that differs from the original MST by exactly one edge. Among all these candidate trees, we take the one with the smallest total weight that is still greater than the original MST weight.

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;

class DSU {
public:
    vector<int> p, r;
    
    // initially each node is its own parent
    DSU(int n) : p(n), r(n, 0) {
        for (int i = 0; i < n; i++) p[i] = i;   
    }
    
    // path compression
    int find(int x) {
        return p[x] == x ? x : p[x] = find(p[x]);   
    }

    bool unite(int a, int b) {
        a = find(a);
        b = find(b);
        
        // already connected
        if (a == b) return false;                  
        
        // union by rank
        if (r[a] < r[b]) swap(a, b);                
        p[b] = a;
        if (r[a] == r[b]) r[a]++;
        return true;
    }
};

// run kruskal while skipping one MST edge
int kruskal(int n, vector<array<int,3>> &edges, int skipU, int skipV) {
    DSU dsu(n);
    int cost = 0, used = 0;

    for (auto &e : edges) {
        int u = e[0], v = e[1], w = e[2];

        // ignore the chosen MST edge
        if ((u == skipU && v == skipV) || (u == skipV && v == skipU))
            continue;

        if (dsu.unite(u, v)) {
            cost += w;
            used++;
        }
    }
    
    // not a valid tree
    if (used != n - 1) return INT_MAX;    
    return cost;
}

int secondBestMST(vector<vector<pair<int,int>>> &adj) {
    int n = adj.size();

    // convert adjacency list to edge list
    vector<array<int,3>> edges;
    for (int u = 0; u < n; u++) {
        for (auto &p : adj[u]) {
            int v = p.first, w = p.second;
            
            // avoid duplicate undirected edges
            if (u < v) edges.push_back({u, v, w});  
        }
    }

    // sort edges by weight once
    sort(edges.begin(), edges.end(),
         [](auto &a, auto &b){ return a[2] < b[2]; });

    // build the MST
    DSU dsu(n);
    int mstCost = 0;
    vector<array<int,3>> mstEdges;

    for (auto &e : edges) {
        if (dsu.unite(e[0], e[1])) {
            mstCost += e[2];
            mstEdges.push_back(e);
        }
    }

    // try removing each MST edge to find second best
    int secondBest = INT_MAX;

    for (auto &e : mstEdges) {
        int newCost = kruskal(n, edges, e[0], e[1]);
        if (newCost > mstCost && newCost < secondBest)
            secondBest = newCost;
    }
    
    return secondBest == INT_MAX ? -1 : secondBest;
}

int main() {

    vector<vector<pair<int,int>>> adj = {
        {{1,4},{2,3}},
        {{0,4},{2,1},{3,5}},
        {{0,3},{1,1},{3,7},{4,10}},
        {{1,5},{2,7},{4,2}},
        {{2,10},{3,2}}
    };

    cout << secondBestMST(adj) << "\n";

    return 0;
}
Java
//Driver Code Starts
import java.util.ArrayList;

class DSU {
    public int[] p, r;
    
    // initially each node is its own parent
    DSU(int n) {
        p = new int[n];
        r = new int[n];
        for (int i = 0; i < n; i++) p[i] = i;
    }
    
    // path compression
    int find(int x) {
        return p[x] == x ? x : (p[x] = find(p[x]));
    }

    boolean unite(int a, int b) {
        a = find(a);
        b = find(b);
        
        // already connected
        if (a == b) return false;                  
        
        // union by rank
        if (r[a] < r[b]) {
            int t = a; a = b; b = t;
        }
        p[b] = a;
        if (r[a] == r[b]) r[a]++;
        return true;
    }
}

class GFG {

//Driver Code Ends

    // run kruskal while skipping one MST edge
    static int kruskal(int n, ArrayList<int[]> edges, int skipU, int skipV) {
        DSU dsu = new DSU(n);
        int cost = 0, used = 0;

        for (int[] e : edges) {
            int u = e[0], v = e[1], w = e[2];

            // ignore the chosen MST edge
            if ((u == skipU && v == skipV) || (u == skipV && v == skipU))
                continue;

            if (dsu.unite(u, v)) {
                cost += w;
                used++;
            }
        }
        
        // not a valid tree
        if (used != n - 1) return Integer.MAX_VALUE;    
        return cost;
    }

    static int secondBestMST(ArrayList<ArrayList<int[]>> adj) {

        int n = adj.size();

        // convert adjacency list to edge list
        ArrayList<int[]> edges = new ArrayList<>();
        for (int u = 0; u < n; u++) {
            for (int[] p : adj.get(u)) {
                int v = p[0], w = p[1];
                
                // avoid duplicate undirected edges
                if (u < v) edges.add(new int[]{u, v, w});  
            }
        }

        // sort edges by weight once
        edges.sort((a, b) -> a[2] - b[2]);

        // build the MST
        DSU dsu = new DSU(n);
        int mstCost = 0;
        ArrayList<int[]> mstEdges = new ArrayList<>();

        for (int[] e : edges) {
            if (dsu.unite(e[0], e[1])) {
                mstCost += e[2];
                mstEdges.add(e);
            }
        }

        // try removing each MST edge to find second best
        int secondBest = Integer.MAX_VALUE;

        for (int[] e : mstEdges) {
            int newCost = kruskal(n, edges, e[0], e[1]);
            if (newCost > mstCost && newCost < secondBest)
                secondBest = newCost;
        }
        
        return secondBest == Integer.MAX_VALUE ? -1 : secondBest;
    }

//Driver Code Starts
    
    static void addEdge(ArrayList<ArrayList<int[]>> adj, int u, int v, int w) {
        
        adj.get(u).add(new int[]{v, w});
        adj.get(v).add(new int[]{u, w});
    }

    public static void main(String[] args) {

        int V = 5;

        ArrayList<ArrayList<int[]>> adj = new ArrayList<>();
        for (int i = 0; i < V; i++)
            adj.add(new ArrayList<>());

        addEdge(adj, 0, 1, 4);
        addEdge(adj, 0, 2, 3);
        addEdge(adj, 1, 2, 1);
        addEdge(adj, 1, 3, 5);
        addEdge(adj, 2, 3, 7);
        addEdge(adj, 2, 4, 10);
        addEdge(adj, 3, 4, 2);

        System.out.println(secondBestMST(adj));
    }
}

//Driver Code Ends
Python
#Driver Code Starts
class DSU:
    def __init__(self, n):
       
        # initially each node is its own parent
        self.p = list(range(n))
        self.r = [0] * n

    # path compression
    def find(self, x):
        if self.p[x] == x:
            return x
        self.p[x] = self.find(self.p[x])
        return self.p[x]

    def unite(self, a, b):
        a = self.find(a)
        b = self.find(b)

        # already connected
        if a == b:
            return False

        # union by rank
        if self.r[a] < self.r[b]:
            a, b = b, a

        self.p[b] = a
        if self.r[a] == self.r[b]:
            self.r[a] += 1

        return True


#Driver Code Ends

# run kruskal while skipping one MST edge
def kruskal(n, edges, skipU, skipV):
    dsu = DSU(n)
    cost = 0
    used = 0

    for u, v, w in edges:

        # ignore the chosen MST edge
        if (u == skipU and v == skipV) or (u == skipV and v == skipU):
            continue

        if dsu.unite(u, v):
            cost += w
            used += 1

    # not a valid tree
    if used != n - 1:
        return float("inf")

    return cost


def secondBestMST(adj):
    n = len(adj)

    # convert adjacency list to edge list
    edges = []
    for u in range(n):
        for v, w in adj[u]:

            # avoid duplicate undirected edges
            if u < v:
                edges.append([u, v, w])

    # sort edges by weight once
    edges.sort(key=lambda x: x[2])

    # build the MST
    dsu = DSU(n)
    mstCost = 0
    mstEdges = []

    for u, v, w in edges:
        if dsu.unite(u, v):
            mstCost += w
            mstEdges.append([u, v, w])

    # try removing each MST edge to find second best
    secondBest = float("inf")

    for u, v, w in mstEdges:
        newCost = kruskal(n, edges, u, v)
        if mstCost < newCost < secondBest:
            secondBest = newCost

    return -1 if secondBest == float("inf") else secondBest

#Driver Code Starts

if __name__ == '__main__':
    adj = [
        [(1,4),(2,3)],
        [(0,4),(2,1),(3,5)],
        [(0,3),(1,1),(3,7),(4,10)],
        [(1,5),(2,7),(4,2)],
        [(2,10),(3,2)]
    ]
    
    print(secondBestMST(adj))

#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;

class DSU {
    public int[] p, r;
    
    // initially each node is its own parent
    public DSU(int n) {
        p = new int[n];
        r = new int[n];
        for (int i = 0; i < n; i++) p[i] = i;
    }
    
    // path compression
    public int find(int x) {
        return p[x] == x ? x : (p[x] = find(p[x]));
    }

    public bool unite(int a, int b) {
        a = find(a);
        b = find(b);
        
        // already connected
        if (a == b) return false;
        
        // union by rank
        if (r[a] < r[b]) {
            int t = a; a = b; b = t;
        }
        p[b] = a;
        if (r[a] == r[b]) r[a]++;
        return true;
    }
}

class GFG {

//Driver Code Ends

    // run kruskal while skipping one MST edge
    static int kruskal(int n, List<int[]> edges, int skipU, int skipV) {
        DSU dsu = new DSU(n);
        int cost = 0, used = 0;

        foreach (var e in edges) {
            int u = e[0], v = e[1], w = e[2];

            // ignore the chosen MST edge
            if ((u == skipU && v == skipV) || (u == skipV && v == skipU))
                continue;

            if (dsu.unite(u, v)) {
                cost += w;
                used++;
            }
        }

        // not a valid tree
        if (used != n - 1) return int.MaxValue;
        return cost;
    }

    static int secondBestMST(List<List<int[]>> adj) {

        int n = adj.Count;

        // convert adjacency list to edge list
        List<int[]> edges = new List<int[]>();
        for (int u = 0; u < n; u++) {
            foreach (var p in adj[u]) {
                int v = p[0], w = p[1];

                // avoid duplicate undirected edges
                if (u < v) edges.Add(new int[]{u, v, w});
            }
        }

        // sort edges by weight once
        edges.Sort((a, b) => a[2].CompareTo(b[2]));

        // build the MST
        DSU dsu = new DSU(n);
        int mstCost = 0;
        List<int[]> mstEdges = new List<int[]>();

        foreach (var e in edges) {
            if (dsu.unite(e[0], e[1])) {
                mstCost += e[2];
                mstEdges.Add(e);
            }
        }

        // try removing each MST edge to find second best
        int secondBest = int.MaxValue;

        foreach (var e in mstEdges) {
            int newCost = kruskal(n, edges, e[0], e[1]);
            if (newCost > mstCost && newCost < secondBest)
                secondBest = newCost;
        }

        return secondBest == int.MaxValue ? -1 : secondBest;
    }

//Driver Code Starts

    static void addEdge(List<List<int[]>> adj, int u, int v, int w) {
        
        adj[u].Add(new int[]{ v, w });
        adj[v].Add(new int[]{ u, w });
    }
    
    static void Main() {
        int V = 5;
    
        // adjacency list
        var adj = new List<List<int[]>>();
        for (int i = 0; i < V; i++)
            adj.Add(new List<int[]>());
    
        addEdge(adj, 0, 1, 4);
        addEdge(adj, 0, 2, 3);
        addEdge(adj, 1, 2, 1);
        addEdge(adj, 1, 3, 5);
        addEdge(adj, 2, 3, 7);
        addEdge(adj, 2, 4, 10);
        addEdge(adj, 3, 4, 2);
    
        Console.WriteLine(secondBestMST(adj));
    }

}

//Driver Code Ends
JavaScript
//Driver Code Starts
class DSU {
    constructor(n) {
        // initially each node is its own parent
        this.p = Array(n).fill(0).map((_, i) => i);
        this.r = Array(n).fill(0);
    }

    // path compression
    find(x) {
        if (this.p[x] === x) return x;
        return this.p[x] = this.find(this.p[x]);
    }

    unite(a, b) {
        a = this.find(a);
        b = this.find(b);

        // already connected
        if (a === b) return false;

        // union by rank
        if (this.r[a] < this.r[b]) [a, b] = [b, a];

        this.p[b] = a;
        if (this.r[a] === this.r[b]) this.r[a]++;
        return true;
    }
}
//Driver Code Ends


// run kruskal while skipping one MST edge
function kruskal(n, edges, skipU, skipV) {
    let dsu = new DSU(n);
    let cost = 0, used = 0;

    for (let [u, v, w] of edges) {

        // ignore the chosen MST edge
        if ((u === skipU && v === skipV) || (u === skipV && v === skipU))
            continue;

        if (dsu.unite(u, v)) {
            cost += w;
            used++;
        }
    }

    // not a valid tree
    if (used !== n - 1) return Infinity;
    return cost;
}

function secondBestMST(adj) {

    let n = adj.length;

    // convert adjacency list to edge list
    let edges = [];
    for (let u = 0; u < n; u++) {
        for (let [v, w] of adj[u]) {

            // avoid duplicate undirected edges
            if (u < v) edges.push([u, v, w]);
        }
    }

    // sort edges by weight once
    edges.sort((a, b) => a[2] - b[2]);

    // build the MST
    let dsu = new DSU(n);
    let mstCost = 0;
    let mstEdges = [];

    for (let e of edges) {
        if (dsu.unite(e[0], e[1])) {
            mstCost += e[2];
            mstEdges.push(e);
        }
    }

    // try removing each MST edge to find second best
    let secondBest = Infinity;

    for (let e of mstEdges) {
        let newCost = kruskal(n, edges, e[0], e[1]);
        if (newCost > mstCost && newCost < secondBest)
            secondBest = newCost;
    }

    return secondBest === Infinity ? -1 : secondBest;
}

//Driver Code Starts

// Driver Code
let adj = [
    [[1,4],[2,3]],
    [[0,4],[2,1],[3,5]],
    [[0,3],[1,1],[3,7],[4,10]],
    [[1,5],[2,7],[4,2]],
    [[2,10],[3,2]]
];

console.log(secondBestMST(adj));

//Driver Code Ends

Output
12

Time Complexity: O(V * E), We sort the edges once in O(E log E) and build the MST in O(E). Then for each of the V−1 MST edges, we rerun Kruskal without that edge, each taking O(E). So the extra work is O(V *E), which dominates everything.
Auxiliary Space: O(V + E)

Comment