GraphProblem 18 of 43

Implement Kruskals Algorithm

Brute Force
Time: O(E^2)
Space: O(V + E)
Optimal
Time: O(E log E)
Space: O(V + E)

Problem Statement

Given a connected, undirected, weighted graph with V vertices and E edges, find the Minimum Spanning Tree (MST) using Kruskal's Algorithm. The MST connects all vertices with the minimum total edge weight.

Example:

  • Input: V = 4, Edges = [[0,1,10],[0,2,6],[0,3,5],[1,3,15],[2,3,4]]
  • Output: MST edges: [2,3,4], [0,3,5], [0,1,10], Total weight = 19

Noob-Friendly Explanation

Imagine you're a city planner connecting several towns with roads. Each road has a building cost (weight). You want to connect ALL towns using the cheapest set of roads — but you don't want any loops (cycles), because that wastes money.

Kruskal's algorithm is simple and greedy:

  1. Sort all roads by cost (cheapest first).
  2. Pick the cheapest road. If it connects two towns that aren't already connected, build it.
  3. If it would create a loop, skip it.
  4. Repeat until all towns are connected.

To check if two towns are already connected, we use Union-Find (a data structure that groups things together).


Approach 1: Brute Force (Without Efficient Union-Find)

Intuition

Sort edges by weight. For each edge, check if adding it creates a cycle by doing a DFS/BFS. If no cycle, include it in the MST.

Algorithm

  1. Sort all edges by weight.
  2. For each edge, use DFS to check if the two vertices are already connected.
  3. If not connected, add the edge to MST.
  4. Stop when we have V-1 edges.
java
import java.util.*;

public class KruskalBruteForce {
    public static List<int[]> kruskalMST(int V, int[][] edges) {
        // Sort edges by weight
        Arrays.sort(edges, (a, b) -> a[2] - b[2]);

        List<int[]> mst = new ArrayList<>();
        List<int[]> currentEdges = new ArrayList<>();
        int totalWeight = 0;

        for (int[] edge : edges) {
            if (mst.size() == V - 1) break;

            // Check if adding this edge creates a cycle using BFS
            if (!isConnected(V, currentEdges, edge[0], edge[1])) {
                mst.add(edge);
                currentEdges.add(edge);
                totalWeight += edge[2];
            }
        }

        System.out.println("Total MST weight: " + totalWeight);
        return mst;
    }

    static boolean isConnected(int V, List<int[]> edges, int src, int dest) {
        List<List<Integer>> adj = new ArrayList<>();
        for (int i = 0; i < V; i++) adj.add(new ArrayList<>());
        for (int[] e : edges) {
            adj.get(e[0]).add(e[1]);
            adj.get(e[1]).add(e[0]);
        }

        boolean[] visited = new boolean[V];
        Queue<Integer> queue = new LinkedList<>();
        visited[src] = true;
        queue.add(src);

        while (!queue.isEmpty()) {
            int node = queue.poll();
            if (node == dest) return true;
            for (int neighbor : adj.get(node)) {
                if (!visited[neighbor]) {
                    visited[neighbor] = true;
                    queue.add(neighbor);
                }
            }
        }
        return false;
    }

    public static void main(String[] args) {
        int V = 4;
        int[][] edges = {{0,1,10},{0,2,6},{0,3,5},{1,3,15},{2,3,4}};
        List<int[]> mst = kruskalMST(V, edges);
        for (int[] e : mst) {
            System.out.println(e[0] + " - " + e[1] + " : " + e[2]);
        }
    }
}

Complexity Analysis

  • Time Complexity: O(E^2) — sorting is O(E log E), but cycle check using BFS is O(V + E) per edge.
  • Space Complexity: O(V + E) — for the adjacency list and visited array.

Approach 2: Optimal (Union-Find / Disjoint Set)

Intuition

Use Union-Find with path compression and union by rank for near O(1) cycle detection. Sort edges, then greedily add edges that connect two different components.

Algorithm

  1. Sort all edges by weight.
  2. Initialize Union-Find for V vertices.
  3. For each edge (in sorted order), if the two vertices belong to different components, union them and add the edge to MST.
  4. Stop when MST has V-1 edges.
java
import java.util.*;

public class KruskalOptimal {
    static int[] parent, rank;

    static int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // path compression
        }
        return parent[x];
    }

    static boolean union(int x, int y) {
        int px = find(x), py = find(y);
        if (px == py) return false;

        if (rank[px] < rank[py]) {
            parent[px] = py;
        } else if (rank[px] > rank[py]) {
            parent[py] = px;
        } else {
            parent[py] = px;
            rank[px]++;
        }
        return true;
    }

    public static List<int[]> kruskalMST(int V, int[][] edges) {
        Arrays.sort(edges, (a, b) -> a[2] - b[2]);

        parent = new int[V];
        rank = new int[V];
        for (int i = 0; i < V; i++) parent[i] = i;

        List<int[]> mst = new ArrayList<>();
        int totalWeight = 0;

        for (int[] edge : edges) {
            if (mst.size() == V - 1) break;

            if (union(edge[0], edge[1])) {
                mst.add(edge);
                totalWeight += edge[2];
            }
        }

        System.out.println("Total MST weight: " + totalWeight);
        return mst;
    }

    public static void main(String[] args) {
        int V = 4;
        int[][] edges = {{0,1,10},{0,2,6},{0,3,5},{1,3,15},{2,3,4}};
        List<int[]> mst = kruskalMST(V, edges);
        for (int[] e : mst) {
            System.out.println(e[0] + " - " + e[1] + " : " + e[2]);
        }
        // Output: 2-3:4, 0-3:5, 0-1:10, Total=19
    }
}

Complexity Analysis

  • Time Complexity: O(E log E) — dominated by sorting the edges. Union-Find operations are nearly O(1).
  • Space Complexity: O(V + E) — for parent, rank arrays and edge list.