Hello! Welcome to our next lesson in the "Core Graph Algorithms" module.
In our last session, we implemented the Bellman-Ford algorithm, which finds the shortest path in graphs, even those with negative edge weights. That algorithm was about finding the most efficient route. Today, we shift our focus from paths to overall network structure by tackling a different kind of optimization problem: finding a Minimum Spanning Tree (MST).
This lesson addresses the learning outcome: Implement Kruskal's algorithm for finding a Minimum Spanning Tree using a Union-Find data structure.
An MST is a subset of the edges of a connected, weighted, undirected graph that connects all the vertices together, without any cycles and with the minimum possible total edge weight. Think of it as designing a network (like laying fiber optic cables or building a road system) to connect several locations with the least amount of material or cost.
Kruskal's algorithm is a classic greedy approach to solve this problem. It's conceptually elegant and highly efficient, making it a cornerstone of network design and optimization problems.
The Greedy Strategy of Kruskal's Algorithm
Kruskal's algorithm works by building the MST piece by piece, always choosing the "cheapest" option available. The high-level strategy is as follows:
- Create a list of all edges in the graph and sort them in non-decreasing order of their weight.
- Iterate through the sorted edges.
- For each edge, if adding it to our growing tree does not form a cycle, we add it. Otherwise, we discard it.
- We stop once we have added edges, where is the number of vertices. At this point, all vertices will be connected.
This greedy choice of picking the smallest available edge at each step is guaranteed to produce a minimum spanning tree.
To see this process in action, the following video provides a clear visual introduction and a step-by-step walkthrough.
Kruskal's Algorithm Visually Explained | Disjoint Sets | Union By Rank | Path Compression
Watch this video by ByteQuest for a quick visual explanation of Kruskal's algorithm. It will give you a solid high-level understanding of the process.
Watch the first part of the video (00:00 - 01:14) to see the algorithm's greedy approach of sorting and selecting edges while avoiding cycles.
This brings up a critical question: how do we efficiently detect if adding an edge creates a cycle? This is where a specialized data structure becomes essential.
Cycle Detection with the Union-Find Data Structure
Checking for cycles every time we add an edge could be very slow if done naively (e.g., with a DFS or BFS). A much more efficient solution is to use the Union-Find data structure, also known as a Disjoint Set Union (DSU).
The core idea is to group vertices into sets, where each set represents a connected component of the forest we are building.
- Initially, each vertex is in its own set.
- When we consider adding an edge
(u, v), we check which setsuandvbelong to. - If
uandvare already in the same set, it means there's already a path between them. Adding the edge(u, v)would create a cycle. So, we discard the edge. - If
uandvare in different sets, adding the edge(u, v)will not create a cycle. It will simply connect two previously disconnected components. So, we add the edge to our MST and merge the two sets into one.
This structure primarily needs two operations:
find(i): Returns an identifier (or "root") for the set containing vertexi.union(i, j): Merges the sets containing verticesiandj.
This next segment of the ByteQuest video explains how disjoint sets are used for cycle detection and introduces the underlying data structures (parent and rank arrays).
Kruskal's Algorithm Visually Explained | Disjoint Sets | Union By Rank | Path Compression
This segment explains the role of disjoint sets in cycle detection and how the data structure is initialized.
Watch from 01:00 to 03:04. Focus on how the find and union operations relate to checking for and preventing cycles.

Optimizing Union-Find
A simple implementation of Union-Find can result in tall, "stringy" trees, making the find operation slow (potentially in the worst case). Two powerful optimizations, Union by Rank and Path Compression, are used to flatten these trees and make operations nearly constant time on average.
- Union by Rank (or Size): When merging two sets, we attach the root of the smaller tree to the root of the larger tree. This keeps the overall tree depth low.
- Path Compression: During a
find(i)operation, after finding the root, we make every node along the path fromito the root point directly to the root. This dramatically flattens the tree for futurefindcalls involving those nodes.
The following video segments provide excellent visual explanations of these crucial optimizations.
Kruskal's Algorithm Visually Explained | Disjoint Sets | Union By Rank | Path Compression
These segments from the ByteQuest video clearly demonstrate the 'Union by Rank' and 'Path Compression' optimizations.
Watch from 03:04 to 05:28. Pay attention to how the parent and rank arrays change during union operations, and how path compression restructures the tree during find operations.
Test your understanding!
Imagine a UnionFind structure for 7 vertices (0 to 6). Initially, each is its own parent.
union(0, 1)union(2, 3)union(0, 2)(Assume rank of 0's root > rank of 2's root)union(4, 5)union(4, 6)(Assume rank of 4's root > rank of 6's root)
Now, if you consider adding the edge (1, 3), will Kruskal's algorithm accept it? Why or why not, based on the find operation?
Show answer
No, the algorithm will reject the edge (1, 3).
Here's why:
- After the
unionoperations, vertices {0, 1, 2, 3} are all in one connected component (one set). - When you call
find(1), it will trace its parent up to the root of its set (let's say it's vertex 0). - When you call
find(3), it will also trace its parent up to the same root (vertex 0). - Since
find(1)andfind(3)return the same root, the algorithm knows that vertices 1 and 3 are already connected. Adding an edge between them would form a cycle.
Implementing Kruskal's Algorithm
Now we can combine the greedy strategy with our optimized Union-Find structure to build a complete implementation. Since you have a background as a software engineer, looking at a well-structured, class-based implementation can be very instructive.
The following article provides a clean Java implementation that encapsulates the logic in Edge, UnionFind, and KruskalAlgorithm classes. This approach is highly modular and reflects good software design principles.
Kruskal's Algorithm in Java Explained
This article, 'Kruskal's Algorithm in Java Explained', gives a complete, production-quality implementation of the algorithm. It's a great reference for seeing how all the pieces fit together.
First, review the 'Cycle Detection with Union-Find' section for the UnionFind class implementation. Then, read 'Data Structures Used' and 'Java Implementation Example' to see the full code. Focus on how the main findMST method orchestrates sorting the edges and using the UnionFind instance to build the result.
The core logic within the main findMST method beautifully demonstrates the algorithm:
- Sort all edges:
Collections.sort(edges); - Initialize Union-Find:
UnionFind uf = new UnionFind(vertices); - Iterate and build:
for (Edge edge : edges) { int root1 = uf.find(edge.src); int root2 = uf.find(edge.dest); if (root1 != root2) { // Check if they are in different sets result.add(edge); // Add edge to MST uf.union(root1, root2); // Merge the sets } }
Complexity Analysis
Understanding the performance characteristics of an algorithm is key to making informed architectural choices. Let's break down the complexity of Kruskal's algorithm, where is the number of vertices and is the number of edges.
-
Time Complexity:
- Edge Sorting: The most computationally expensive step is sorting the list of edges. This takes time using an efficient sorting algorithm like Merge Sort or Quick Sort.
- Union-Find Operations: We then iterate through each of the edges. For each edge, we perform up to two
findoperations and oneunionoperation. With the path compression and union by rank optimizations, the amortized time for each operation is nearly constant, denoted as , where is the extremely slow-growing inverse Ackermann function. For all practical purposes, you can think of this as . - Therefore, the total time for this phase is roughly .
- The overall complexity is the sum of these parts: . Since is much larger than , the sorting step dominates. Thus, the final time complexity is .
-
Space Complexity:
- We need space to store the list of edges.
- The
UnionFinddata structure requires space for its parent and rank arrays. - Therefore, the total space complexity is .
The video from take U forward also has a good summary of the complexity analysis.
G-47. Kruskal's Algorithm - Minimum Spanning Tree - C++ and Java
For a verbal walkthrough of the complexity analysis, this segment is a helpful supplement.
Watch the final segment on time and space complexity (11:10 - 12:37) to reinforce your understanding.
Conclusion
In this lesson, you've mastered Kruskal's algorithm, a fundamental tool for network optimization. This moves you beyond simple pathfinding into problems of structural connectivity.
Key Takeaways:
- Kruskal's algorithm finds a Minimum Spanning Tree by greedily adding the lowest-weight edges that do not form a cycle.
- The Union-Find (or Disjoint Set) data structure is the key to performing efficient cycle detection.
- Path Compression and Union by Rank are critical optimizations that make Union-Find operations nearly constant time on average.
- The algorithm's performance bottleneck is sorting the edges, leading to a time complexity of .
Preview of the Next Lesson:
We will continue our exploration of Minimum Spanning Trees by implementing Prim's algorithm. While Kruskal's builds a "forest" of components that eventually merge, Prim's algorithm grows a single tree from a starting vertex, much like Dijkstra's algorithm. We will compare and contrast their approaches, performance, and ideal use cases.
