Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added MST Algorithms to the Algorithms_and_Data_Structures folder #481 #593

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = []

# Function to add an edge to the graph
def addEdge(self, u, v, w):
self.graph.append([u, v, w])

# A function to find the subset of an element i (with path compression)
def find(self, parent, i):
if parent[i] != i:
parent[i] = self.find(parent, parent[i])
return parent[i]

# A function that does union of two subsets x and y (uses union by rank)
def union(self, parent, rank, x, y):
xroot = self.find(parent, x)
yroot = self.find(parent, y)

# Attach smaller rank tree under root of higher rank tree
if rank[xroot] < rank[yroot]:
parent[xroot] = yroot
elif rank[xroot] > rank[yroot]:
parent[yroot] = xroot
else:
parent[yroot] = xroot
rank[xroot] += 1

# Function to construct MST using Kruskal's algorithm
def KruskalMST(self):
result = []

# Step 1: Sort all the edges in non-decreasing order of their weight
self.graph = sorted(self.graph, key=lambda item: item[2])

parent = []
rank = []

# Create V subsets with single elements
for node in range(self.V):
parent.append(node)
rank.append(0)

e = 0 # Number of edges in MST
i = 0 # Index variable used for sorted edges

# Number of edges in MST will be V-1
while e < self.V - 1:

# Step 2: Pick the smallest edge and increment the index for next iteration
u, v, w = self.graph[i]
i += 1

x = self.find(parent, u)
y = self.find(parent, v)

# If including this edge doesn't cause a cycle, include it in result
if x != y:
e += 1
result.append([u, v, w])
self.union(parent, rank, x, y)

return result

if __name__ == "__main__":
# Input number of vertices
V = int(input("Enter the number of vertices: "))
g = Graph(V)

# Input number of edges
E = int(input("Enter the number of edges: "))

# Input the edges
print("Enter the edges in the format 'u v w' where u and v are vertices, and w is the weight:")
for _ in range(E):
u, v, w = map(int, input().split())
g.addEdge(u, v, w)

# Get the Minimum Spanning Tree (MST) using Kruskal's algorithm
mst = g.KruskalMST()

# Output the MST
print("Edges in the Minimum Spanning Tree:")
for u, v, weight in mst:
print(f"{u} -- {v} == {weight}")
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import heapq
from collections import defaultdict

class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = defaultdict(list)

def addEdge(self, u, v, w):
# Adding edge (u, v) with weight w
self.graph[u].append((v, w))
self.graph[v].append((u, w))

def PrimMST(self):
result = []
# Set to keep track of visited vertices
visited = set()
# Priority queue to pick the edge with the smallest weight
pq = [(0, 0)] # (weight, vertex)

while pq:
# Get the vertex with the smallest weight
weight, u = heapq.heappop(pq)
if u in visited:
continue
#Mark this vertex as visited
visited.add(u)
#Add the vertex and the weight to the result
result.append((u, weight))

#Traverse all the adjacent vertices of u
for v, w in self.graph[u]:
if v not in visited:
#Push adjacent vertices to the priority queue
heapq.heappush(pq, (w, v))

# Returning the MST result
return result

if __name__ == "__main__":
# Take number of vertices as input
V = int(input("Enter the number of vertices in the graph: "))

g = Graph(V)

# Take number of edges as input
E = int(input("Enter the number of edges in the graph: "))

# Input the edges (u, v, w) from the user
print("Enter the edges in the format 'u,v,w' where u and v are vertices and w is the weight:")
for _ in range(E):
u, v, w = map(int, input().split())
g.addEdge(u, v, w)

# Get the Minimum Spanning Tree (MST)
mst = g.PrimMST()

# Output
print("Minimum Spanning Tree:", mst)
59 changes: 59 additions & 0 deletions Algorithms_and_Data_Structures/Minimum Spanning Tree/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

# Minimum Spanning Tree (MST) Algorithms
This project demonstrates three algorithms for finding the Minimum Spanning Tree (MST) in a graph. A Minimum Spanning Tree is a subgraph that connects all vertices with the minimum total edge weight, without any cycles.

### Algorithms included:
- Prim's Algorithm
- Kruskal's Algorithm
- SciPy's Minimum Spanning Tree
## 1. Prim's Algorithm
**Description:** Prim’s algorithm grows the MST one edge at a time. It starts from an arbitrary vertex and adds the smallest edge connecting the growing MST to a new vertex.

**How it works:**
- Begin with any node as the starting point.
- Repeatedly add the smallest edge connecting a visited node to an unvisited node.
- Stop when all vertices are included in the MST.

**Time Complexity:**
- Using an adjacency matrix and a simple priority queue (heap), the time complexity is` O(V²)`, where V is the number of vertices.
- If using an adjacency list and a min-heap, the time complexity is `O(E log V)`, where E is the number of edges.
- Usage: Prim’s algorithm is efficient for dense graphs (many edges). It efficiently adds the next closest vertex to the existing tree.

## 2. Kruskal's Algorithm
**Description:** Kruskal’s algorithm sorts all the edges by weight and builds the MST by adding the smallest edge, ensuring no cycles are formed. It uses a Union-Find data structure to detect cycles.

**How it works:**
- Sort all edges by their weights.
- Pick the smallest edge and add it to the MST if it doesn’t form a cycle.
- Stop when the MST contains `V-1` edges (for a graph with V vertices).

**Time Complexity:**
- O(E log E), which simplifies to O(E log V), since sorting the edges takes O(E log E) and each find and union operation in the Union-Find structure takes nearly constant time, `O(log*V)`.
- Usage: Kruskal’s algorithm is efficient for sparse graphs (few edges) and uses sorting and union-find to ensure efficiency.

## 3. Minimum Spanning Tree using SciPy
**Description:** SciPy provides a built-in function to compute the Minimum Spanning Tree using the Compressed Sparse Row (CSR) matrix representation of a graph.

**How it works:**
- The graph is represented as a sparse matrix, where non-zero entries represent edge weights.
- SciPy’s minimum_spanning_tree() function efficiently computes the MST.

**Time Complexity:**
- For sparse graphs, using SciPy’s implementation, the time complexity is typically `O(E log V)`.
- Usage: This method is very efficient when working with large, sparse graphs. The sparse matrix format saves memory and speeds up computation.

### **Installation and Requirements**<br>
To run the algorithms, you will need:
- Python 3.x
- `SciPy` for the SciPy Minimum Spanning Tree

To install SciPy, use:
``` python
pip install scipy
```
## Conclusion
These three algorithms are fundamental for solving the Minimum Spanning Tree problem, each suited for different graph structures:

1. Prim’s algorithm is ideal for dense graphs.
2. Kruskal’s algorithm works well for sparse graphs.
3. SciPy’s MST function is an efficient option for large, sparse graphs.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import minimum_spanning_tree

# Create a sparse matrix representing the graph
graph = csr_matrix([[0, 2, 0, 6, 0],
[2, 0, 3, 8, 5],
[0, 3, 0, 0, 7],
[6, 8, 0, 0, 9],
[0, 5, 7, 9, 0]])

# Compute the MST
mst = minimum_spanning_tree(graph)

# Print the MST
print(mst.toarray().astype(int))