A spanning tree of a graph is a subgraph that connects all the vertices together without any cycles and with the minimum possible number of edges. Essentially, a spanning tree includes all the vertices of the graph, but only the necessary edges to ensure connectivity, without creating loops. Spanning trees are fundamental in various applications, including network design, circuit design, and algorithms such as Minimum Spanning Tree (MST), which plays a crucial role in optimizing networks.
In a graph , where:
A spanning tree is a subgraph of , where:
A Minimum Spanning Tree (MST) is a spanning tree of a connected, undirected graph where the sum of the weights of the edges is minimized. If the graph has weighted edges, MST algorithms help to find the spanning tree with the least total weight.
Two well-known algorithms used to find the MST of a graph are Kruskal’s Algorithm and Prim’s Algorithm.
Kruskal’s Algorithm is a greedy algorithm used to find the Minimum Spanning Tree (MST) of a graph. It works by considering edges in increasing order of their weight and adding them to the MST, provided they don’t form a cycle.
Steps of Kruskal’s Algorithm:
Time Complexity: , where is the number of edges.
Example:
Consider the following weighted graph:
A
/ \
1 3
/ \
B-------C
2
Step-by-step Kruskal’s Algorithm:
MST: .
Prim’s Algorithm is another greedy algorithm that grows the MST one edge at a time. It starts with an arbitrary vertex and repeatedly adds the smallest edge that connects a vertex in the MST to a vertex outside the MST.
Steps of Prim’s Algorithm:
Time Complexity: , where is the number of edges and is the number of vertices.
Example:
Consider the same graph from above:
A
/ \
1 3
/ \
B-------C
2
MST: .
Below is an example of how to implement Kruskal’s algorithm to find the minimum spanning tree in Python:
# Kruskal’s Algorithm implementation for Minimum Spanning Tree
class DisjointSet:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, u):
if self.parent[u] != u:
self.parent[u] = self.find(self.parent[u])
return self.parent[u]
def union(self, u, v):
root_u = self.find(u)
root_v = self.find(v)
if root_u != root_v:
if self.rank[root_u] > self.rank[root_v]:
self.parent[root_v] = root_u
elif self.rank[root_u] < self.rank[root_v]:
self.parent[root_u] = root_v
else:
self.parent[root_v] = root_u
self.rank[root_u] += 1
class Kruskal:
def __init__(self, vertices, edges):
self.V = vertices
self.edges = edges
def run(self):
mst = []
disjoint_set = DisjointSet(self.V)
sorted_edges = sorted(self.edges, key=lambda x: x[2])
for u, v, weight in sorted_edges:
if disjoint_set.find(u) != disjoint_set.find(v):
mst.append((u, v, weight))
disjoint_set.union(u, v)
return mst
# Example graph: (u, v, weight)
edges = [
(0, 1, 1),
(0, 2, 3),
(1, 2, 2)
]
# Number of vertices in the graph
vertices = 3
kruskal = Kruskal(vertices, edges)
mst = kruskal.run()
print("Minimum Spanning Tree:")
for u, v, weight in mst:
print(f"Edge: ({u}, {v}) with weight {weight}")
Output
Minimum Spanning Tree:
Edge: (0, 1) with weight 1
Edge: (1, 2) with weight 2