Skip to content

Commit

Permalink
added Boruvka's MST algorithm (TheAlgorithms#2026)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
3 people authored May 25, 2020
1 parent bb5552e commit 0e61906
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 0 deletions.
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
* [Greedy Best First](https://github.com/TheAlgorithms/Python/blob/master/graphs/greedy_best_first.py)
* [Kahns Algorithm Long](https://github.com/TheAlgorithms/Python/blob/master/graphs/kahns_algorithm_long.py)
* [Kahns Algorithm Topo](https://github.com/TheAlgorithms/Python/blob/master/graphs/kahns_algorithm_topo.py)
* [Minimum Spanning Tree Boruvka](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_boruvka.py)
* [Minimum Spanning Tree Kruskal](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_kruskal.py)
* [Minimum Spanning Tree Prims](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_prims.py)
* [Multi Heuristic Astar](https://github.com/TheAlgorithms/Python/blob/master/graphs/multi_heuristic_astar.py)
Expand Down
195 changes: 195 additions & 0 deletions graphs/minimum_spanning_tree_boruvka.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
class Graph:
"""
Data structure to store graphs (based on adjacency lists)
"""

def __init__(self):

self.num_vertices = 0
self.num_edges = 0
self.adjacency = {}

def add_vertex(self, vertex):
"""
Adds a vertex to the graph
"""
if vertex not in self.adjacency:
self.adjacency[vertex] = {}
self.num_vertices += 1

def add_edge(self, head, tail, weight):
"""
Adds an edge to the graph
"""

self.add_vertex(head)
self.add_vertex(tail)

if head == tail:
return

self.adjacency[head][tail] = weight
self.adjacency[tail][head] = weight

def distinct_weight(self):
"""
For Boruvks's algorithm the weights should be distinct
Converts the weights to be distinct
"""
edges = self.get_edges()
for edge in edges:
head, tail, weight = edge
edges.remove((tail, head, weight))
for i in range(len(edges)):
edges[i] = list(edges[i])

edges.sort(key=lambda e: e[2])
for i in range(len(edges) - 1):
if edges[i][2] >= edges[i + 1][2]:
edges[i + 1][2] = edges[i][2] + 1
for edge in edges:
head, tail, weight = edge
self.adjacency[head][tail] = weight
self.adjacency[tail][head] = weight

def __str__(self):
"""
Returns string representation of the graph
"""
string = ""
for tail in self.adjacency:
for head in self.adjacency[tail]:
weight = self.adjacency[head][tail]
string += "%d -> %d == %d\n" % (head, tail, weight)
return string.rstrip("\n")

def get_edges(self):
"""
Returna all edges in the graph
"""
output = []
for tail in self.adjacency:
for head in self.adjacency[tail]:
output.append((tail, head, self.adjacency[head][tail]))
return output

def get_vertices(self):
"""
Returns all vertices in the graph
"""
return self.adjacency.keys()

@staticmethod
def build(vertices=None, edges=None):
"""
Builds a graph from the given set of vertices and edges
"""
g = Graph()
if vertices is None:
vertices = []
if edges is None:
edge = []
for vertex in vertices:
g.add_vertex(vertex)
for edge in edges:
g.add_edge(*edge)
return g

class UnionFind(object):
"""
Disjoint set Union and Find for Boruvka's algorithm
"""

def __init__(self):
self.parent = {}
self.rank = {}

def __len__(self):
return len(self.parent)

def make_set(self, item):
if item in self.parent:
return self.find(item)

self.parent[item] = item
self.rank[item] = 0
return item

def find(self, item):
if item not in self.parent:
return self.make_set(item)
if item != self.parent[item]:
self.parent[item] = self.find(self.parent[item])
return self.parent[item]

def union(self, item1, item2):
root1 = self.find(item1)
root2 = self.find(item2)

if root1 == root2:
return root1

if self.rank[root1] > self.rank[root2]:
self.parent[root2] = root1
return root1

if self.rank[root1] < self.rank[root2]:
self.parent[root1] = root2
return root2

if self.rank[root1] == self.rank[root2]:
self.rank[root1] += 1
self.parent[root2] = root1
return root1

def boruvka_mst(graph):
"""
Implementation of Boruvka's algorithm
>>> g = Graph()
>>> g = Graph.build([0, 1, 2, 3], [[0, 1, 1], [0, 2, 1],[2, 3, 1]])
>>> g.distinct_weight()
>>> bg = Graph.boruvka_mst(g)
>>> print(bg)
1 -> 0 == 1
2 -> 0 == 2
0 -> 1 == 1
0 -> 2 == 2
3 -> 2 == 3
2 -> 3 == 3
"""
num_components = graph.num_vertices

union_find = Graph.UnionFind()
mst_edges = []
while num_components > 1:
cheap_edge = {}
for vertex in graph.get_vertices():
cheap_edge[vertex] = -1

edges = graph.get_edges()
for edge in edges:
head, tail, weight = edge
edges.remove((tail, head, weight))
for edge in edges:
head, tail, weight = edge
set1 = union_find.find(head)
set2 = union_find.find(tail)
if set1 != set2:
if cheap_edge[set1] == -1 or cheap_edge[set1][2] > weight:
cheap_edge[set1] = [head, tail, weight]

if cheap_edge[set2] == -1 or cheap_edge[set2][2] > weight:
cheap_edge[set2] = [head, tail, weight]
for vertex in cheap_edge:
if cheap_edge[vertex] != -1:
head, tail, weight = cheap_edge[vertex]
if union_find.find(head) != union_find.find(tail):
union_find.union(head, tail)
mst_edges.append(cheap_edge[vertex])
num_components = num_components - 1
mst = Graph.build(edges=mst_edges)
return mst

0 comments on commit 0e61906

Please sign in to comment.