-
-
Notifications
You must be signed in to change notification settings - Fork 46.9k
Added Borůvka's algorithm. #4645
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
Changes from 12 commits
8d67fd5
93e2298
89cf0a4
d8b6132
88bb91e
c4a2e46
ac6a863
c01ccb9
daa532c
3265907
0867092
f8125d7
e9af60a
aba7767
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
"""Borůvka's algorithm. | ||
|
||
Determines the minimum spanning tree(MST) of a graph using the Borůvka's algorithm. | ||
Borůvka's algorithm is a greedy algorithm for finding a minimum spanning tree in a | ||
graph,or a minimum spanning forest in the case of a graph that is not connected. | ||
|
||
The time complexity of this algorithm is O(ELogV), where E represents the number | ||
of edges, while V represents the number of nodes. | ||
|
||
The space complexity of this algorithm is O(V + E), since we have to keep a couple | ||
of lists whose sizes are equal to the number of nodes, as well as keep all the | ||
edges of a graph inside of the data structure itself. | ||
|
||
Borůvka's algorithm gives us pretty much the same result as other MST Algorithms - | ||
they all find the minimum spanning tree, and the time complexity is approximately | ||
the same. | ||
|
||
One advantage that Borůvka's algorithm has compared to the alternatives is that it | ||
doesn't need to presort the edges or maintain a priority queue in order to find the | ||
minimum spanning tree. | ||
Even though that doesn't help its complexity, since it still passes the edges logE | ||
times, it is a bit more simple to code. | ||
|
||
Details: https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm | ||
""" | ||
|
||
|
||
class Graph: | ||
"""Class Graph.""" | ||
|
||
def __init__(self, num_of_nodes: int) -> None: | ||
""" | ||
Arguments: | ||
num_of_nodes - the number of nodes in the graph | ||
Attributes: | ||
m_v - the number of nodes in the graph. | ||
m_edges - the list of edges. | ||
m_component - the dictionary which stores the index of the component which | ||
a node belongs to. | ||
""" | ||
|
||
self.m_v = num_of_nodes | ||
self.m_edges = [] | ||
self.m_component = {} | ||
|
||
def add_edge(self, u_node: int, v_node: int, weight: int) -> None: | ||
"""Adds an edge in the format [first, second, edge weight] to graph.""" | ||
|
||
self.m_edges.append([u_node, v_node, weight]) | ||
|
||
def find_component(self, u_node: int) -> int: | ||
"""Propagates a new component throughout a given component.""" | ||
|
||
if self.m_component[u_node] == u_node: | ||
return u_node | ||
return self.find_component(self.m_component[u_node]) | ||
|
||
def set_component(self, u_node: int) -> None: | ||
"""Finds the component index of a given node""" | ||
|
||
if self.m_component[u_node] == u_node: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be just |
||
return | ||
else: | ||
for k in self.m_component.keys(): | ||
self.m_component[k] = self.find_component(k) | ||
|
||
def union(self, component_size: list, u_node: int, v_node: int) -> None: | ||
"""Union finds the roots of components for two nodes, compares the components | ||
in terms of size, and attaches the smaller one to the larger one to form | ||
single component""" | ||
|
||
if component_size[u_node] <= component_size[v_node]: | ||
self.m_component[u_node] = v_node | ||
component_size[v_node] += component_size[u_node] | ||
self.set_component(u_node) | ||
|
||
elif component_size[u_node] >= component_size[v_node]: | ||
self.m_component[v_node] = self.find_component(u_node) | ||
component_size[u_node] += component_size[v_node] | ||
self.set_component(v_node) | ||
|
||
print(self.m_component) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this print statement |
||
|
||
def boruvka(self) -> None: | ||
"""Performs Borůvka's algorithm to find MST.""" | ||
|
||
# Initialize additional lists required to algorithm. | ||
component_size = [] | ||
mst_weight = 0 | ||
|
||
minimum_weight_edge = [-1] * self.m_v | ||
|
||
# A list of components (initialized to all of the nodes) | ||
for node in range(self.m_v): | ||
self.m_component.update({node: node}) | ||
component_size.append(1) | ||
|
||
num_of_components = self.m_v | ||
|
||
while num_of_components > 1: | ||
for i in range(len(self.m_edges)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling |
||
|
||
u = self.m_edges[i][0] | ||
v = self.m_edges[i][1] | ||
w = self.m_edges[i][2] | ||
|
||
u_component = self.m_component[u] | ||
v_component = self.m_component[v] | ||
|
||
if u_component != v_component: | ||
"""If the current minimum weight edge of component u doesn't | ||
exist (is -1), or if it's greater than the edge we're | ||
observing right now, we will assign the value of the edge | ||
we're observing to it. | ||
|
||
If the current minimum weight edge of component v doesn't | ||
exist (is -1), or if it's greater than the edge we're | ||
observing right now, we will assign the value of the edge | ||
we're observing to it""" | ||
|
||
if ( | ||
minimum_weight_edge[u_component] == -1 | ||
or minimum_weight_edge[u_component][2] > w | ||
): | ||
minimum_weight_edge[u_component] = [u, v, w] | ||
if ( | ||
minimum_weight_edge[v_component] == -1 | ||
or minimum_weight_edge[v_component][2] > w | ||
): | ||
minimum_weight_edge[v_component] = [u, v, w] | ||
|
||
for node in range(self.m_v): | ||
if minimum_weight_edge[node] != -1: | ||
u = minimum_weight_edge[node][0] | ||
v = minimum_weight_edge[node][1] | ||
w = minimum_weight_edge[node][2] | ||
|
||
u_component = self.m_component[u] | ||
v_component = self.m_component[v] | ||
|
||
if u_component != v_component: | ||
mst_weight += w | ||
self.union(component_size, u_component, v_component) | ||
print( | ||
"Added edge [" | ||
+ str(u) | ||
+ " - " | ||
+ str(v) | ||
+ "]\n" | ||
+ "Added weight: " | ||
+ str(w) | ||
+ "\n" | ||
) | ||
num_of_components -= 1 | ||
|
||
minimum_weight_edge = [-1] * self.m_v | ||
print("The total weight of the minimal spanning tree is: " + str(mst_weight)) | ||
|
||
|
||
def test_vector() -> None: | ||
""" | ||
>>> g=Graph(8) | ||
>>> g.add_edge(0, 1, 10) | ||
>>> g.add_edge(0, 2, 6) | ||
>>> g.add_edge(0, 3, 5) | ||
>>> g.add_edge(1, 3, 15) | ||
>>> g.add_edge(2, 3, 4) | ||
>>> g.add_edge(3, 4, 8) | ||
>>> g.add_edge(4, 5, 10) | ||
>>> g.add_edge(4, 6, 6) | ||
>>> g.add_edge(4, 7, 5) | ||
>>> g.add_edge(5, 7, 15) | ||
>>> g.add_edge(6, 7, 4) | ||
>>> g.boruvka() | ||
{0: 3, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7} | ||
Added edge [0 - 3] | ||
Added weight: 5 | ||
<BLANKLINE> | ||
{0: 3, 1: 3, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7} | ||
Added edge [0 - 1] | ||
Added weight: 10 | ||
<BLANKLINE> | ||
{0: 3, 1: 3, 2: 3, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7} | ||
Added edge [2 - 3] | ||
Added weight: 4 | ||
<BLANKLINE> | ||
{0: 3, 1: 3, 2: 3, 3: 3, 4: 7, 5: 5, 6: 6, 7: 7} | ||
Added edge [4 - 7] | ||
Added weight: 5 | ||
<BLANKLINE> | ||
{0: 3, 1: 3, 2: 3, 3: 3, 4: 7, 5: 7, 6: 6, 7: 7} | ||
Added edge [4 - 5] | ||
Added weight: 10 | ||
<BLANKLINE> | ||
{0: 3, 1: 3, 2: 3, 3: 3, 4: 7, 5: 7, 6: 7, 7: 7} | ||
Added edge [6 - 7] | ||
Added weight: 4 | ||
<BLANKLINE> | ||
{0: 7, 1: 7, 2: 7, 3: 7, 4: 7, 5: 7, 6: 7, 7: 7} | ||
Added edge [3 - 4] | ||
Added weight: 8 | ||
<BLANKLINE> | ||
The total weight of the minimal spanning tree is: 46 | ||
""" | ||
|
||
|
||
if __name__ == "__main__": | ||
import doctest | ||
|
||
doctest.testmod() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary comment.