Quick Info

Category: Greedy
Time Complexity: O(E log E)
Space Complexity: O(V)
Input Type: Weighted Graph

Kruskal's Algorithm

Description

Kruskal's algorithm is a minimum spanning tree algorithm that finds an edge of the least possible weight that connects any two trees in the forest. It is a greedy algorithm as it finds a minimum spanning tree for a connected weighted graph adding increasing cost arcs at each step.

How It Works

  1. Sort all edges in non-decreasing order of their weight
  2. Initialize a forest where each vertex is a separate tree
  3. For each edge in sorted order:
    • If including the edge doesn't create a cycle:
      • Add it to the spanning tree
      • Union the sets of vertices it connects
  4. Continue until spanning tree has (V-1) edges

Visualization

Click Start to begin visualization

Implementation


class UnionFind:
    def __init__(self, size):
        self.parent = list(range(size))
        self.rank = [0] * size
    
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])  # Path compression
        return self.parent[x]
    
    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py:
            return False
        
        # Union by rank
        if self.rank[px] < self.rank[py]:
            px, py = py, px
        self.parent[py] = px
        if self.rank[px] == self.rank[py]:
            self.rank[px] += 1
        return True

class Kruskal:
    @staticmethod
    def minimum_spanning_tree(vertices: int, edges: list[tuple[int, int, int]]) -> list[tuple[int, int, int]]:
        """
        Find the minimum spanning tree using Kruskal's algorithm.
        
        Args:
            vertices: Number of vertices in the graph
            edges: List of edges as tuples (from_vertex, to_vertex, weight)
            
        Returns:
            List of edges in the minimum spanning tree
        """
        # Sort edges by weight
        edges = sorted(edges, key=lambda x: x[2])
        
        # Initialize Union-Find data structure
        uf = UnionFind(vertices)
        
        mst = []
        total_weight = 0
        
        # Process edges in ascending order of weight
        for u, v, weight in edges:
            if uf.union(u, v):
                mst.append((u, v, weight))
                total_weight += weight
                
                # Stop when we have V-1 edges
                if len(mst) == vertices - 1:
                    break
        
        return mst

# Example usage
if __name__ == "__main__":
    # Example graph
    V = 4
    edges = [
        (0, 1, 10),  # Edge from vertex 0 to 1 with weight 10
        (0, 2, 6),
        (0, 3, 5),
        (1, 3, 15),
        (2, 3, 4)
    ]
    
    mst = Kruskal.minimum_spanning_tree(V, edges)
    
    print("Minimum Spanning Tree edges:")
    total_weight = 0
    for u, v, weight in mst:
        print(f"Edge {u}-{v}: weight = {weight}")
        total_weight += weight
    print(f"Total MST weight: {total_weight}")
                                        

#include <iostream>
#include <vector>
#include <algorithm>

class UnionFind {
private:
    std::vector<int> parent;
    std::vector<int> rank;
    
public:
    UnionFind(int size) : parent(size), rank(size, 0) {
        for (int i = 0; i < size; ++i) {
            parent[i] = i;
        }
    }
    
    int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);  // Path compression
        }
        return parent[x];
    }
    
    bool union_sets(int x, int y) {
        int px = find(x), py = find(y);
        if (px == py) return false;
        
        // Union by rank
        if (rank[px] < rank[py]) {
            std::swap(px, py);
        }
        parent[py] = px;
        if (rank[px] == rank[py]) {
            rank[px]++;
        }
        return true;
    }
};

class Kruskal {
public:
    struct Edge {
        int from, to, weight;
        
        Edge(int f, int t, int w) : from(f), to(t), weight(w) {}
        
        bool operator<(const Edge& other) const {
            return weight < other.weight;
        }
    };
    
    static std::vector<Edge> minimum_spanning_tree(int vertices, std::vector<Edge>& edges) {
        // Sort edges by weight
        std::sort(edges.begin(), edges.end());
        
        UnionFind uf(vertices);
        std::vector<Edge> mst;
        int total_weight = 0;
        
        for (const Edge& edge : edges) {
            if (uf.union_sets(edge.from, edge.to)) {
                mst.push_back(edge);
                total_weight += edge.weight;
                
                if (mst.size() == vertices - 1) {
                    break;
                }
            }
        }
        
        return mst;
    }
};

int main() {
    // Example graph
    int V = 4;
    std::vector<Kruskal::Edge> edges = {
        {0, 1, 10},  // Edge from vertex 0 to 1 with weight 10
        {0, 2, 6},
        {0, 3, 5},
        {1, 3, 15},
        {2, 3, 4}
    };
    
    auto mst = Kruskal::minimum_spanning_tree(V, edges);
    
    std::cout << "Minimum Spanning Tree edges:\n";
    int total_weight = 0;
    for (const auto& edge : mst) {
        std::cout << "Edge " << edge.from << "-" << edge.to 
                  << ": weight = " << edge.weight << "\n";
        total_weight += edge.weight;
    }
    std::cout << "Total MST weight: " << total_weight << "\n";
    
    return 0;
}
                                        

using System;
using System.Collections.Generic;
using System.Linq;

public class UnionFind
{
    private int[] parent;
    private int[] rank;
    
    public UnionFind(int size)
    {
        parent = Enumerable.Range(0, size).ToArray();
        rank = new int[size];
    }
    
    public int Find(int x)
    {
        if (parent[x] != x)
        {
            parent[x] = Find(parent[x]);  // Path compression
        }
        return parent[x];
    }
    
    public bool Union(int x, int y)
    {
        int px = Find(x), py = Find(y);
        if (px == py) return false;
        
        // Union by rank
        if (rank[px] < rank[py])
        {
            (px, py) = (py, px);
        }
        parent[py] = px;
        if (rank[px] == rank[py])
        {
            rank[px]++;
        }
        return true;
    }
}

public class Kruskal
{
    public record Edge(int From, int To, int Weight);
    
    public static List<Edge> MinimumSpanningTree(int vertices, List<Edge> edges)
    {
        // Sort edges by weight
        edges.Sort((a, b) => a.Weight.CompareTo(b.Weight));
        
        var uf = new UnionFind(vertices);
        var mst = new List<Edge>();
        int totalWeight = 0;
        
        foreach (var edge in edges)
        {
            if (uf.Union(edge.From, edge.To))
            {
                mst.Add(edge);
                totalWeight += edge.Weight;
                
                if (mst.Count == vertices - 1)
                {
                    break;
                }
            }
        }
        
        return mst;
    }
    
    public static void Main()
    {
        // Example graph
        int V = 4;
        var edges = new List<Edge>
        {
            new(0, 1, 10),  // Edge from vertex 0 to 1 with weight 10
            new(0, 2, 6),
            new(0, 3, 5),
            new(1, 3, 15),
            new(2, 3, 4)
        };
        
        var mst = MinimumSpanningTree(V, edges);
        
        Console.WriteLine("Minimum Spanning Tree edges:");
        int totalWeight = 0;
        foreach (var edge in mst)
        {
            Console.WriteLine($"Edge {edge.From}-{edge.To}: weight = {edge.Weight}");
            totalWeight += edge.Weight;
        }
        Console.WriteLine($"Total MST weight: {totalWeight}");
    }
}
                                        

Complexity Analysis

Time Complexity

O(E log E) where:

  • E is the number of edges
  • Sorting edges takes O(E log E)
  • Union-Find operations take O(α(V)), which is nearly constant

Space Complexity

O(V) where:

  • V is the number of vertices
  • Space needed for the disjoint-set data structure
  • Additional O(E) space for storing sorted edges

Advantages and Disadvantages

Advantages

  • Simple to implement
  • Works well with sparse graphs
  • Guarantees minimum spanning tree
  • Can be easily parallelized
  • Good for distributed systems

Disadvantages

  • Not as efficient for dense graphs
  • Requires sorting all edges
  • Requires Union-Find data structure
  • Not suitable for dynamic graphs
  • Memory intensive for large graphs

Applications

  • Network design (computer networks, transportation)
  • Circuit design in electronics
  • Water supply networks
  • Clustering algorithms
  • Approximation algorithms for NP-hard problems