Second Best Minimum Spanning Tree (original) (raw)

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

[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 #include #include #include using namespace std;

class DSU { public: vector 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

`

**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)