Minimum Spanning Tree (Kruskal)

Problem

The first line has n and m: n nodes (0 to n-1) and m undirected edges. The next m lines have a b w: an edge between a and b with the weight w. Print the smallest total weight of a set of edges that connects all nodes. Print disconnected if the nodes cannot all be connected. Edges can be repeated with different weights.

Constraints

  • 1 <= n <= 10^5
  • 0 <= m <= 2 * 10^5
  • 1 <= w <= 10^6

Examples

Input

4 5
0 1 10
0 2 6
0 3 5
1 3 15
2 3 4

Output

19

Solutions

Best approachTime O(m log m)Space O(n + m)

Kruskal's greedy rule: take the cheapest edge that does not create a cycle. Union-Find answers the cycle question in almost constant time. Sorting the edges dominates the time.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Minimum spanning tree.
 * Input   : n and m, then m weighted edges.
 * Output  : The total weight or disconnected.
 * Approach: Kruskal.
 * Time    : O(m log m)
 * Space   : O(n + m)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Find the root of a node. Path halving keeps the paths short.
        static int find(int[] parent, int node) {
            while (parent[node] != node) {
                parent[node] = parent[parent[node]];
                node = parent[node];
            }
            return node;
        }

    // This method solves the task. It receives the input and returns the answer.
    static String solve(String[] lines) {
        String[] header = lines[0].split(" ");
        int n = Integer.parseInt(header[0]);
        int m = Integer.parseInt(header[1]);

        // Step 1: Read the edges as {weight, a, b} and sort them from the cheapest to the most expensive.
        long[][] edges = new long[m][];
        for (int i = 0; i < m; i++) {
            String[] part = lines[i + 1].split(" ");
            edges[i] = new long[] {Long.parseLong(part[2]), Long.parseLong(part[0]), Long.parseLong(part[1])};
        }
        java.util.Arrays.sort(edges, (x, y) -> Long.compare(x[0], y[0]));

        // Step 2: Union-Find keeps the groups of nodes that are already connected.
        int[] parent = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }

        long total = 0;
        int used = 0;
        for (long[] edge : edges) {
            int rootA = find(parent, (int) edge[1]);
            int rootB = find(parent, (int) edge[2]);

            // Step 3: Take an edge only if it joins two different groups. Then it cannot make a cycle.
            if (rootA != rootB) {
                parent[rootA] = rootB;
                total += edge[0];
                used++;
            }
        }

        // Step 4: A spanning tree of n nodes has exactly n - 1 edges. Fewer edges means the graph is not connected.
        return used == n - 1 ? String.valueOf(total) : "disconnected";
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        java.util.List<String> all = new java.util.ArrayList<>();
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine().trim();
            if (!line.isEmpty()) {
                all.add(line);
            }
        }
        String[] lines = all.toArray(new String[0]);
        System.out.println(solve(lines));
    }
}
Hints (1)
  1. How do we know that an edge would create a cycle?