Skip to content

Commit 0e61906

Browse files
nitishabharathigithub-actionscclauss
authored
added Boruvka's MST algorithm (TheAlgorithms#2026)
* added Boruvka's MST algorithm * Add files via upload * fixup! Format Python code with psf/black push * Updated Boruvka with doctest * updating DIRECTORY.md * Update minimum_spanning_tree_boruvka.py * No blank line in doctest * <BLANKLINE> * Avoid mutable default values https://docs.python-guide.org/writing/gotchas/ * Update minimum_spanning_tree_boruvka.py * Avoid mutable default values * fixup! Format Python code with psf/black push * Update minimum_spanning_tree_boruvka.py * Update minimum_spanning_tree_boruvka.py Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss <cclauss@me.com>
1 parent bb5552e commit 0e61906

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed

DIRECTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@
246246
* [Greedy Best First](https://github.com/TheAlgorithms/Python/blob/master/graphs/greedy_best_first.py)
247247
* [Kahns Algorithm Long](https://github.com/TheAlgorithms/Python/blob/master/graphs/kahns_algorithm_long.py)
248248
* [Kahns Algorithm Topo](https://github.com/TheAlgorithms/Python/blob/master/graphs/kahns_algorithm_topo.py)
249+
* [Minimum Spanning Tree Boruvka](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_boruvka.py)
249250
* [Minimum Spanning Tree Kruskal](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_kruskal.py)
250251
* [Minimum Spanning Tree Prims](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_prims.py)
251252
* [Multi Heuristic Astar](https://github.com/TheAlgorithms/Python/blob/master/graphs/multi_heuristic_astar.py)
+195
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
class Graph:
2+
"""
3+
Data structure to store graphs (based on adjacency lists)
4+
"""
5+
6+
def __init__(self):
7+
8+
self.num_vertices = 0
9+
self.num_edges = 0
10+
self.adjacency = {}
11+
12+
def add_vertex(self, vertex):
13+
"""
14+
Adds a vertex to the graph
15+
16+
"""
17+
if vertex not in self.adjacency:
18+
self.adjacency[vertex] = {}
19+
self.num_vertices += 1
20+
21+
def add_edge(self, head, tail, weight):
22+
"""
23+
Adds an edge to the graph
24+
25+
"""
26+
27+
self.add_vertex(head)
28+
self.add_vertex(tail)
29+
30+
if head == tail:
31+
return
32+
33+
self.adjacency[head][tail] = weight
34+
self.adjacency[tail][head] = weight
35+
36+
def distinct_weight(self):
37+
"""
38+
For Boruvks's algorithm the weights should be distinct
39+
Converts the weights to be distinct
40+
41+
"""
42+
edges = self.get_edges()
43+
for edge in edges:
44+
head, tail, weight = edge
45+
edges.remove((tail, head, weight))
46+
for i in range(len(edges)):
47+
edges[i] = list(edges[i])
48+
49+
edges.sort(key=lambda e: e[2])
50+
for i in range(len(edges) - 1):
51+
if edges[i][2] >= edges[i + 1][2]:
52+
edges[i + 1][2] = edges[i][2] + 1
53+
for edge in edges:
54+
head, tail, weight = edge
55+
self.adjacency[head][tail] = weight
56+
self.adjacency[tail][head] = weight
57+
58+
def __str__(self):
59+
"""
60+
Returns string representation of the graph
61+
"""
62+
string = ""
63+
for tail in self.adjacency:
64+
for head in self.adjacency[tail]:
65+
weight = self.adjacency[head][tail]
66+
string += "%d -> %d == %d\n" % (head, tail, weight)
67+
return string.rstrip("\n")
68+
69+
def get_edges(self):
70+
"""
71+
Returna all edges in the graph
72+
"""
73+
output = []
74+
for tail in self.adjacency:
75+
for head in self.adjacency[tail]:
76+
output.append((tail, head, self.adjacency[head][tail]))
77+
return output
78+
79+
def get_vertices(self):
80+
"""
81+
Returns all vertices in the graph
82+
"""
83+
return self.adjacency.keys()
84+
85+
@staticmethod
86+
def build(vertices=None, edges=None):
87+
"""
88+
Builds a graph from the given set of vertices and edges
89+
90+
"""
91+
g = Graph()
92+
if vertices is None:
93+
vertices = []
94+
if edges is None:
95+
edge = []
96+
for vertex in vertices:
97+
g.add_vertex(vertex)
98+
for edge in edges:
99+
g.add_edge(*edge)
100+
return g
101+
102+
class UnionFind(object):
103+
"""
104+
Disjoint set Union and Find for Boruvka's algorithm
105+
"""
106+
107+
def __init__(self):
108+
self.parent = {}
109+
self.rank = {}
110+
111+
def __len__(self):
112+
return len(self.parent)
113+
114+
def make_set(self, item):
115+
if item in self.parent:
116+
return self.find(item)
117+
118+
self.parent[item] = item
119+
self.rank[item] = 0
120+
return item
121+
122+
def find(self, item):
123+
if item not in self.parent:
124+
return self.make_set(item)
125+
if item != self.parent[item]:
126+
self.parent[item] = self.find(self.parent[item])
127+
return self.parent[item]
128+
129+
def union(self, item1, item2):
130+
root1 = self.find(item1)
131+
root2 = self.find(item2)
132+
133+
if root1 == root2:
134+
return root1
135+
136+
if self.rank[root1] > self.rank[root2]:
137+
self.parent[root2] = root1
138+
return root1
139+
140+
if self.rank[root1] < self.rank[root2]:
141+
self.parent[root1] = root2
142+
return root2
143+
144+
if self.rank[root1] == self.rank[root2]:
145+
self.rank[root1] += 1
146+
self.parent[root2] = root1
147+
return root1
148+
149+
def boruvka_mst(graph):
150+
"""
151+
Implementation of Boruvka's algorithm
152+
>>> g = Graph()
153+
>>> g = Graph.build([0, 1, 2, 3], [[0, 1, 1], [0, 2, 1],[2, 3, 1]])
154+
>>> g.distinct_weight()
155+
>>> bg = Graph.boruvka_mst(g)
156+
>>> print(bg)
157+
1 -> 0 == 1
158+
2 -> 0 == 2
159+
0 -> 1 == 1
160+
0 -> 2 == 2
161+
3 -> 2 == 3
162+
2 -> 3 == 3
163+
"""
164+
num_components = graph.num_vertices
165+
166+
union_find = Graph.UnionFind()
167+
mst_edges = []
168+
while num_components > 1:
169+
cheap_edge = {}
170+
for vertex in graph.get_vertices():
171+
cheap_edge[vertex] = -1
172+
173+
edges = graph.get_edges()
174+
for edge in edges:
175+
head, tail, weight = edge
176+
edges.remove((tail, head, weight))
177+
for edge in edges:
178+
head, tail, weight = edge
179+
set1 = union_find.find(head)
180+
set2 = union_find.find(tail)
181+
if set1 != set2:
182+
if cheap_edge[set1] == -1 or cheap_edge[set1][2] > weight:
183+
cheap_edge[set1] = [head, tail, weight]
184+
185+
if cheap_edge[set2] == -1 or cheap_edge[set2][2] > weight:
186+
cheap_edge[set2] = [head, tail, weight]
187+
for vertex in cheap_edge:
188+
if cheap_edge[vertex] != -1:
189+
head, tail, weight = cheap_edge[vertex]
190+
if union_find.find(head) != union_find.find(tail):
191+
union_find.union(head, tail)
192+
mst_edges.append(cheap_edge[vertex])
193+
num_components = num_components - 1
194+
mst = Graph.build(edges=mst_edges)
195+
return mst

0 commit comments

Comments
 (0)