mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-27 15:01:08 +00:00
added Boruvka's MST algorithm (#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>
This commit is contained in:
parent
bb5552efd0
commit
0e619065e7
|
@ -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)
|
||||
|
|
195
graphs/minimum_spanning_tree_boruvka.py
Normal file
195
graphs/minimum_spanning_tree_boruvka.py
Normal file
|
@ -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
|
Loading…
Reference in New Issue
Block a user