Skip to content

Commit 9dfebc6

Browse files
author
Michele De Vita
committed
changed implementation of kruskal algorithm to union-find data structure to decrease time complexity. Write doc and adapt setup.py to pep8 style guide
1 parent 660fb6a commit 9dfebc6

File tree

6 files changed

+95
-84
lines changed

6 files changed

+95
-84
lines changed

docs/Minumum spanning tree.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=======================
2+
Minimum spanning tree
3+
=======================
4+
5+
6+
Given a weighted connected graph (a graph with weights on the edges), the minimum spanning tree is a spanning tree that
7+
minimize the weights of the edges.
8+
9+
10+
==========================
11+
Features
12+
==========================
13+
14+
* Algorithms implemented:
15+
- Kruskal
16+
17+
* To create the Minimum spanning tree use the *minimum_spanning_tree* function, it takes in input the list of weighted edges, that it is represented in python as a tuple composed by the edge, a tuple of two vertexes identified by numbers, and the weight, a number (e.g. ((2,5), 5))
18+
19+
* Code example:
20+
21+
.. code-block:: python
22+
23+
from pygorithm.minimum_spanning_tree import kruskal
24+
edges_weighted = [((1,2), 4), ((2, 4), 5), ((1,3), 13), ((2, 3), 9), ((1, 4), 1)]
25+
kruskal.minimum_spanning_tree(edges_weighted) #[((1, 4), 1), ((1, 2), 4), ((2, 3), 9)]

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Table of Contents:
1515

1616
Sorting
1717
Searching
18+
Minimum spanning tree
1819

1920

2021
-----------------
Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,49 @@
1-
import copy
1+
"""
2+
Kruskal algorithm for minimum spanning tree in a weighted graph with union–find data structure
3+
"""
4+
__author__ = "Michele De Vita <[email protected]>"
5+
__date__ = "14/07/2017"
26

37

4-
def get_nodes(edges_weighted: list):
8+
def get_vertexes(edges_weighted: list):
59
return set(sum((edge for edge, weight in edges_weighted), ()))
610

711

8-
def is_reversed(curr_edge, edge):
9-
return curr_edge[0] == edge[1] and curr_edge[1] == edge[0]
12+
def set_of(vertex, forest):
13+
for tree in forest:
14+
if vertex in tree:
15+
return tree
16+
return None
1017

1118

12-
def has_cycle(edges_w):
13-
edges_w += [((b, a), weight) for (a, b), weight in edges_w]
14-
for edge, weight in edges_w:
15-
if rec_cycle_detector(edge, edge, copy.deepcopy(edges_w), 1):
16-
return True
17-
return False
18-
19-
20-
def rec_cycle_detector(s_edge, curr_edge, edges_w, step=None):
21-
if step > 2 and curr_edge[1] == s_edge[0]:
22-
return True
23-
neightbours = ((edge, weight) for edge, weight in edges_w if
24-
curr_edge[1] == edge[0] and not is_reversed(curr_edge, edge))
25-
for neightbour in neightbours:
26-
edges_w.remove(neightbour)
27-
if rec_cycle_detector(s_edge, neightbour[0], edges_w, step + 1):
28-
return True
29-
return False
30-
31-
32-
def vertex_of(edge_w: tuple) -> set:
33-
return set(edge_w[0])
19+
def union(u_set, v_set, forest):
20+
forest.remove(u_set)
21+
forest.remove(v_set)
22+
forest.append(v_set + u_set)
23+
return forest
3424

3525

3626
def minimum_spanning_tree(edges_weighted: list):
3727
"""
38-
:param edges_weighted: A list of pair with first element the edge as tuple (e.g. (1,4))
39-
and the weight as second element
40-
:return:
28+
:param edges_weighted: A list of pair with first element the edge as tuple
29+
and the weight as second element (e.g. ((1,4), 5) )
30+
:return: the list of edges explored
4131
"""
4232
edges_weighted.sort(key=lambda pair: pair[1])
43-
edges_list = []
44-
e_vertexes = set()
45-
vertexes = get_nodes(edges_weighted)
46-
while vertexes != e_vertexes:
47-
edge_w = edges_weighted.pop(0)
48-
if (len(edges_list) > 2 and not has_cycle(edges_list + [edge_w])) or len(edges_list) <= 2:
49-
e_vertexes.update(vertex_of(edge_w))
50-
edges_list.append(edge_w)
51-
return edges_list
33+
edges_explored = []
34+
forest = [[v] for v in get_vertexes(edges_weighted)]
35+
for (u, v), weight in edges_weighted:
36+
u_set, v_set = set_of(u, forest), set_of(v, forest)
37+
if u_set != v_set:
38+
forest = union(u_set, v_set, forest)
39+
edges_explored.append(((u, v), weight))
40+
return edges_explored
41+
42+
43+
def time_complexities():
44+
return '''Worst case: O(E log(V)) where E in the number of edges and V the number of vertexes'''
45+
46+
47+
def get_code():
48+
import inspect
49+
return inspect.getsource(minimum_spanning_tree)

setup.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@
1010
long_description = f.read()
1111

1212
setup(
13-
name = 'pygorithm',
14-
version = '0.1.dev2',
15-
description = 'A Python algorithms module for learning',
16-
long_description = long_description,
13+
name='pygorithm',
14+
version='0.1.dev2',
15+
description='A Python algorithms module for learning',
16+
long_description=long_description,
1717
# The project's main homepage.
1818
url='https://github.com/OmkarPathak/pygorithms',
1919
# Author details
20-
author = 'Omkar Pathak',
21-
author_email = '[email protected]',
20+
author='Omkar Pathak',
21+
author_email='[email protected]',
2222
# Choose your license
23-
license = 'MIT',
23+
license='MIT',
2424
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
25-
classifiers = [
25+
classifiers=[
2626
# Indicate who your project is intended for
2727
'Intended Audience :: Developers',
2828
'Topic :: Software Development :: Libraries',
@@ -38,5 +38,5 @@
3838
'Programming Language :: Python :: 3.4',
3939
'Programming Language :: Python :: 3.5',
4040
],
41-
packages = find_packages()
41+
packages=find_packages()
4242
)

tests/kruskal_mst_tests.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import unittest
2+
3+
from pygorithm.minimun_spanning_tree import kruskal
4+
5+
6+
class KruskalTest(unittest.TestCase):
7+
8+
def test_minimum_spanning_tree(self):
9+
"""
10+
test inspired from the example at the following link: https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
11+
"""
12+
edges_weighted = [((1, 2), 7), ((2, 3), 8), ((1, 4), 5), ((2, 4), 9),
13+
((2, 5), 7), ((3, 5), 5), ((4, 6), 6), ((5, 6), 8),
14+
((5, 7), 9), ((6, 7), 11), ((4, 5), 15)]
15+
expected = [((1, 4), 5), ((3, 5), 5), ((4, 6), 6), ((1, 2), 7), ((2, 5), 7), ((5, 7), 9)]
16+
self.assertEqual(kruskal.minimum_spanning_tree(edges_weighted), expected)
17+
18+
def test_minimum_spanning_tree_2(self):
19+
"""
20+
Test inspired by the gif at the left of the page https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
21+
"""
22+
edges_weighted = [((1,2), 3), ((1, 5), 1), ((2, 5), 4), ((2, 3), 5), ((3, 5), 6), ((3, 4), 2), ((4, 5), 7)]
23+
expected = [((1, 5), 1), ((3, 4), 2), ((1, 2), 3), ((2, 3), 5)]
24+
self.assertEqual(kruskal.minimum_spanning_tree(edges_weighted), expected)

tests/mst_weighted_graph_tests.py

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)