Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Graphs / Spanning Trees

4.4.2 Minimum Spanning Tree (Kruskal)

4-Graphs/4.4.2_Minimum_Spanning_Tree_(Kruskal).cpp

For a connected, undirected, weighted graph with possibly negative weights, a minimum spanning tree (MST) connects all nodes using a subset of edges with minimum possible total weight. On disconnected input, this implementation instead finds the minimum spanning forest.

Kruskal's algorithm scans the edges in nondecreasing weight order, adding each edge whose endpoints lie in different components (tracked with a disjoint-set structure) and skipping any edge that would form a cycle.

  • kruskal_mst(n) populates mst with the edge IDs in the minimum spanning forest and returns the total MST weight for a global, pre-populated edge list edges whose endpoints must be numbered $[0, {\htmlClass{math-inline-code}{\texttt{n}}})$. Each edge is stored as a tuple (weight, u, v).

Multigraphs are supported; parallel edges appear as separate entries in edges and any redundant ones are skipped once the cheaper edge has already connected the two components.

Implementation

#include <algorithm>
#include <cstdint>
#include <numeric>
#include <tuple>
#include <utility>
#include <vector>

std::vector<std::tuple<int, int, int>> edges;  // (weight, u, v)
std::vector<int> dsu_root, dsu_size, mst;

int find(int u) {
  return dsu_root[u] == u ? u : dsu_root[u] = find(dsu_root[u]);
}

int64_t kruskal_mst(int n) {
  mst.clear();
  std::vector<int> order(edges.size());
  std::iota(order.begin(), order.end(), 0);
  std::sort(order.begin(), order.end(), [](int a, int b) {
    return std::get<0>(edges[a]) < std::get<0>(edges[b]);
  });
  dsu_root.assign(n, 0);
  dsu_size.assign(n, 1);
  std::iota(dsu_root.begin(), dsu_root.end(), 0);
  int64_t total_weight = 0;
  for (int id : order) {
    auto [w, u, v] = edges[id];
    u = find(u);
    v = find(v);
    if (u != v) {
      if (dsu_size[u] < dsu_size[v]) {
        std::swap(u, v);
      }
      dsu_root[v] = u;
      dsu_size[u] += dsu_size[v];
      mst.push_back(id);
      total_weight += w;  // Overflow warning.
    }
  }
  return total_weight;
}

Example Usage

#include <cassert>
using namespace std;

int main() {
  // Two connected components; MST routine returns a minimum spanning forest.
  //      w=4              w=1
  //   0 ----- 1        3 ----- 4
  //    \     /               / |
  // w=3 \   / w=6      w=2 /   | w=4
  //      \ /             / w=3 |
  //       2            5 ----- 6
  edges.emplace_back(4, 0, 1);  // (weight, u, v)
  edges.emplace_back(6, 1, 2);
  edges.emplace_back(3, 2, 0);
  edges.emplace_back(1, 3, 4);
  edges.emplace_back(2, 4, 5);
  edges.emplace_back(3, 5, 6);
  edges.emplace_back(4, 6, 4);
  assert(kruskal_mst(7) == 13);
  assert((mst == vector<int>{3, 4, 2, 5, 0}));
  return 0;
}