Minimum Spanning Tree Kruskal's Algorithm in DSA Python - Time & Space Complexity
We want to understand how the time needed to find a minimum spanning tree using Kruskal's algorithm changes as the graph grows.
How does the number of edges and vertices affect the work done?
Analyze the time complexity of the following code snippet.
class DisjointSet:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
rootX = self.find(x)
rootY = self.find(y)
if rootX != rootY:
if self.rank[rootX] < self.rank[rootY]:
self.parent[rootX] = rootY
elif self.rank[rootX] > self.rank[rootY]:
self.parent[rootY] = rootX
else:
self.parent[rootY] = rootX
self.rank[rootX] += 1
def kruskal(edges, n):
edges.sort(key=lambda x: x[2]) # sort by weight
ds = DisjointSet(n)
mst = []
for u, v, w in edges:
if ds.find(u) != ds.find(v):
ds.union(u, v)
mst.append((u, v, w))
return mst
This code finds the minimum spanning tree of a graph with n vertices and edges given as (u, v, weight).
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Sorting all edges by weight.
- How many times: Sorting runs once on all edges, which is m edges.
- Secondary operation: For each edge, find and union operations on disjoint sets.
- How many times: Up to m times, once per edge.
As the number of edges (m) and vertices (n) grow, sorting edges takes more time, and union-find operations also increase.
| Input Size (n vertices, m edges) | Approx. Operations |
|---|---|
| 10, 15 | Sorting ~15 log 15, Union-Find ~15 |
| 100, 500 | Sorting ~500 log 500, Union-Find ~500 |
| 1000, 5000 | Sorting ~5000 log 5000, Union-Find ~5000 |
Pattern observation: Sorting dominates and grows a bit faster than linearly with edges; union-find grows linearly with edges.
Time Complexity: O(m log m)
This means the time mainly depends on sorting the edges, which grows a bit faster than the number of edges.
[X] Wrong: "The union-find operations take as much time as sorting."
[OK] Correct: Union-find operations are almost constant time on average due to path compression and union by rank, so sorting edges is the real time cost.
Understanding Kruskal's time complexity helps you explain how efficient your solution is and shows you can analyze algorithms beyond just coding them.
"What if the edges were already sorted? How would the time complexity change?"
Practice
Solution
Step 1: Understand Kruskal's algorithm purpose
Kruskal's algorithm is designed to find a subset of edges that connects all vertices with the minimum total edge weight.Step 2: Compare with other graph problems
Shortest path, cycle detection, and max flow are different problems with different algorithms.Final Answer:
To find the minimum spanning tree of a graph -> Option BQuick Check:
Kruskal's algorithm = Minimum Spanning Tree [OK]
- Confusing MST with shortest path
- Thinking it detects cycles only
- Mixing with max flow problems
Solution
Step 1: Identify edge structure
Edges are usually stored as tuples (node1, node2, weight), so weight is at index 2.Step 2: Use sort with key on weight
Sorting by weight means sorting by the third element, so key=lambda x: x[2] is correct.Final Answer:
edges.sort(key=lambda x: x[2]) -> Option AQuick Check:
Sort edges by weight = key=lambda x: x[2] [OK]
- Sorting by wrong tuple index
- Using sort() without key when edges are tuples
- Confusing node indices with weight index
[(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)]?
edges = [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)]
edges.sort(key=lambda x: x[2])
parent = list(range(4))
def find(x):
while parent[x] != x:
x = parent[x]
return x
def union(a, b):
rootA = find(a)
rootB = find(b)
if rootA != rootB:
parent[rootB] = rootA
return True
return False
mst = []
for u, v, w in edges:
if union(u, v):
mst.append((u, v, w))
print(mst)Solution
Step 1: Sort edges by weight
Sorted edges: (2,3,4), (0,3,5), (0,2,6), (0,1,10), (1,3,15)Step 2: Apply union-find to select edges without cycles
Pick (2,3,4), union success; pick (0,3,5), union success; pick (0,2,6), union fails (cycle); pick (0,1,10), union success; stop as MST has 3 edges for 4 nodes.Final Answer:
[(2, 3, 4), (0, 3, 5), (0, 1, 10)] -> Option DQuick Check:
Edges chosen = minimum weights without cycles [OK]
- Not sorting edges correctly
- Adding edges that form cycles
- Stopping too early or too late
edges = [(0,1,1), (1,2,2), (0,2,3)]
parent = [0, 1, 2]
def find(x):
if parent[x] == x:
return x
return find(parent[x])
def union(a, b):
rootA = find(a)
rootB = find(b)
if rootA != rootB:
parent[a] = b # Bug here
return True
return False
mst = []
for u,v,w in sorted(edges, key=lambda x: x[2]):
if union(u,v):
mst.append((u,v,w))
print(mst)Solution
Step 1: Analyze union function
Union should link roots of sets, not original nodes directly.Step 2: Fix parent assignment
Assign parent of rootB to rootA to merge sets correctly: 'parent[rootB] = rootA'.Final Answer:
Change 'parent[a] = b' to 'parent[rootB] = rootA' -> Option AQuick Check:
Union sets by root parents, not nodes [OK]
- Assigning parent of node instead of root
- Confusing rootA and rootB in assignment
- Skipping path compression in find
[(0,1,2), (0,3,6), (1,2,3), (1,3,8), (1,4,5), (2,4,7), (3,4,9)], what is the total weight of the minimum spanning tree found by Kruskal's algorithm?Solution
Step 1: Sort edges by weight
Sorted edges: (0,1,2), (1,2,3), (1,4,5), (0,3,6), (2,4,7), (1,3,8), (3,4,9)Step 2: Select edges without cycles until all vertices connected
Pick (0,1,2), (1,2,3), (1,4,5), (0,3,6). Total weight = 2+3+5+6 = 16. But check if (2,4,7) creates cycle: yes, skip. (1,3,8) and (3,4,9) create cycles, skip.Step 3: Verify MST completeness
All 5 vertices connected with 4 edges, total weight 16.Final Answer:
16 -> Option CQuick Check:
MST total weight = sum of chosen edges [OK]
- Including edges that form cycles
- Missing edges needed to connect all vertices
- Adding heavier edges unnecessarily
Ready to prove it?
Pass the test and this pattern moves unknown
