Skip to content

Commit 033665f

Browse files
author
Michele De Vita
committed
Moved implentation of kruskal into WeightedGraph class into data_structures/graph.py and refactor relative tests. Changed some implementation details into Graph class to simplify the code into add_edge method. Changed some code in linked_list and modules to respect pep8 and make some methods (especially get_code()) static or class dependent
1 parent e6c5e08 commit 033665f

File tree

6 files changed

+164
-79
lines changed

6 files changed

+164
-79
lines changed

pygorithm/data_structures/graph.py

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,99 @@
11
# Author: OMKAR PATHAK
22
# Created On: 12th August 2017
3+
from collections import defaultdict
4+
35

46
class Graph(object):
57
def __init__(self):
6-
self.graph = {}
8+
self.graph = defaultdict(list)
79
self.count = 0
810

911
def print_graph(self):
1012
''' for printing the contents of the graph '''
11-
for i in self.graph:
12-
print(i,'->',' -> '.join([str(j) for j in self.graph[i]]))
13+
for i in self.graph:
14+
print(i, '->', ' -> '.join([str(j) for j in self.graph[i]]))
1315

1416
def add_edge(self, from_vertex, to_vertex):
1517
''' function to add an edge in the graph '''
1618
# check if vertex is already present
17-
if from_vertex in self.graph.keys():
18-
self.graph[from_vertex].append(to_vertex)
19-
self.count += 1
20-
else:
21-
self.graph[from_vertex] = [to_vertex]
22-
self.graph[to_vertex] = []
23-
self.count += 1
19+
self.graph[from_vertex].append(to_vertex)
20+
self.count += 1
2421

2522
def get_code(self):
26-
''' returns the code for the current class '''
23+
""" returns the code for the current class """
2724
import inspect
2825
return inspect.getsource(Graph)
2926

27+
28+
class WeightedGraph:
29+
"""
30+
A graph with a numerical value (weight) on edges
31+
"""
32+
def __init__(self):
33+
self.edges_weighted = []
34+
self.vertexes = set()
35+
36+
def add_edge(self, u, v, weight):
37+
"""
38+
:param u: from vertex - type : integer
39+
:param v: to vertex - type : integer
40+
:param weight: weight of the edge - type : numeric
41+
"""
42+
edge = ((u, v), weight)
43+
self.edges_weighted.append(edge)
44+
self.vertexes.update((u, v))
45+
46+
def print_graph(self):
47+
for (u, v), weight in self.edges_weighted:
48+
print("%d -> %d weight: %d" % (u, v, weight))
49+
50+
def _set_of(self, vertex):
51+
for tree in self.forest:
52+
if vertex in tree:
53+
return tree
54+
return None
55+
56+
def _union(self, u_set, v_set):
57+
self.forest.remove(u_set)
58+
self.forest.remove(v_set)
59+
self.forest.append(v_set + u_set)
60+
61+
def kruskal_mst(self):
62+
"""
63+
Kruskal algorithm for finding the minimum spanning tree of a weighted graph.
64+
This version use a union-find data structure.
65+
More detailed info here: https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
66+
Author: Michele De Vita <[email protected]>
67+
"""
68+
# sort by weight
69+
self.edges_weighted.sort(key=lambda pair: pair[1])
70+
edges_explored = []
71+
self.forest = [[v] for v in self.vertexes]
72+
for (u, v), weight in self.edges_weighted:
73+
u_set, v_set = self._set_of(u), self._set_of(v)
74+
if u_set != v_set:
75+
self._union(u_set, v_set)
76+
edges_explored.append(((u, v), weight))
77+
return edges_explored
78+
79+
@staticmethod
80+
def kruskal_complexity():
81+
return '''Worst case: O(E log(V)) where E in the number of edges and V the number of vertexes'''
82+
83+
@classmethod
84+
def kruskal_code(cls):
85+
import inspect
86+
return inspect.getsource(cls.kruskal_mst)
87+
88+
3089
class TopologicalSort(Graph):
3190
def topological_sort(self):
3291
''' function for sorting graph elements using topological sort '''
33-
visited = [False] * self.count # Marking all vertices as not visited
34-
stack = [] # Stack for storing the vertex
92+
visited = [False] * self.count # Marking all vertices as not visited
93+
stack = [] # Stack for storing the vertex
3594
for vertex in range(self.count):
3695
# Call the recursive function only if not visited
37-
if visited[vertex] == False:
96+
if not visited[vertex]:
3897
self.topological_sort_rec(vertex, visited, stack)
3998

4099
return stack
@@ -53,22 +112,23 @@ def topological_sort_rec(self, vertex, visited, stack):
53112
return
54113

55114
# Push current vertex to stack which stores the result
56-
stack.insert(0,vertex)
115+
stack.insert(0, vertex)
57116

58117
def get_code(self):
59118
''' returns the code for the current class '''
60119
import inspect
61120
return inspect.getsource(TopologicalSort)
62121

122+
63123
class CheckCycleDirectedGraph(object):
64124
def __init__(self):
65125
self.graph = {}
66126
self.count = 0
67127

68128
def print_graph(self):
69129
''' for printing the contents of the graph '''
70-
for i in self.graph:
71-
print(i,'->',' -> '.join([str(j) for j in self.graph[i]]))
130+
for i in self.graph:
131+
print(i, '->', ' -> '.join([str(j) for j in self.graph[i]]))
72132

73133
def add_edge(self, from_vertex, to_vertex):
74134
''' function to add an edge in the graph '''
@@ -114,18 +174,19 @@ def get_code(self):
114174
import inspect
115175
return inspect.getsource(CheckCycleDirected)
116176

177+
117178
class CheckCycleUndirectedGraph(object):
118179
def __init__(self):
119180
self.graph = {}
120181
self.count = 0
121182

122183
def print_graph(self):
123184
''' for printing the contents of the graph '''
124-
for i in self.graph:
125-
print(i,'->',' -> '.join([str(j) for j in self.graph[i]]))
185+
for i in self.graph:
186+
print(i, '->', ' -> '.join([str(j) for j in self.graph[i]]))
126187

127188
def add_edge(self, fromVertex, toVertex):
128-
''' for adding the edge beween two vertices '''
189+
''' for adding the edge between two vertices '''
129190
# check if vertex is already present,
130191
if fromVertex in self.graph.keys() and toVertex in self.graph.keys():
131192
self.graph[fromVertex].append(toVertex)
@@ -137,7 +198,7 @@ def add_edge(self, fromVertex, toVertex):
137198

138199
def check_cycle(self):
139200
''' This function will return True if graph is cyclic else return False '''
140-
visited = [False] * len(self.graph) # Marking all vertices as not visited
201+
visited = [False] * len(self.graph) # Marking all vertices as not visited
141202
for vertex in range(len(self.graph)):
142203
# Call the recursive function only if not visited
143204
if visited[vertex] == False:

pygorithm/data_structures/heap.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from pygorithm.data_structures import queue
66

7+
78
# min-heap implementation as priority queue
89
class Heap(queue.Queue):
910
def parent_idx(self, idx):
@@ -17,7 +18,7 @@ def right_child_idx(self, idx):
1718

1819
def insert(self, data):
1920
super().enqueue(data)
20-
if self.rear >= 1: # heap may need to be fixed
21+
if self.rear >= 1: # heap may need to be fixed
2122
self.heapify_up()
2223

2324
def heapify_up(self):
@@ -31,7 +32,7 @@ def heapify_up(self):
3132
Best Case: O(1), item is inserted at correct position, no swaps needed
3233
Worst Case: O(logn), item needs to be swapped throughout all levels of tree
3334
'''
34-
child = self.rear
35+
child = self.rear
3536
parent = self.parent_idx(child)
3637
while self.queue[child] < self.queue[self.parent_idx(child)]:
3738
# Swap (sift up) and update child:parent relation
@@ -42,23 +43,23 @@ def heapify_up(self):
4243
def pop(self):
4344
''' Removes the lowest value element (highest priority, at root) from the heap '''
4445
min = super().dequeue()
45-
if self.rear >= 1: # heap may need to be fixed
46+
if self.rear >= 1: # heap may need to be fixed
4647
self.heapify_down()
4748
return min
4849

4950
def favorite(self, parent):
5051
''' Determines which child has the highest priority by 3 cases '''
51-
left = self.left_child_idx(parent)
52+
left = self.left_child_idx(parent)
5253
right = self.right_child_idx(parent)
5354

54-
if left <= self.rear and right <= self.rear: # case 1: both nodes exist
55+
if left <= self.rear and right <= self.rear: # case 1: both nodes exist
5556
if self.queue[left] <= self.queue[right]:
5657
return left
5758
else:
5859
return right
59-
elif left <= self.rear: # case 2: only left exists
60+
elif left <= self.rear: # case 2: only left exists
6061
return left
61-
else: # case 3: no children (if left doesn't exist, neither can the right)
62+
else: # case 3: no children (if left doesn't exist, neither can the right)
6263
return None
6364

6465
def heapify_down(self):
@@ -71,8 +72,8 @@ def heapify_down(self):
7172
Best Case: O(1), item is inserted at correct position, no swaps needed
7273
Worst Case: O(logn), item needs to be swapped throughout all levels of tree
7374
'''
74-
cur = ROOT = 0 # start at the root
75-
fav = self.favorite(cur) # determine favorite child
75+
cur = ROOT = 0 # start at the root
76+
fav = self.favorite(cur) # determine favorite child
7677
while self.queue[fav] is not None:
7778
if self.queue[cur] > self.queue[fav]:
7879
# Swap (sift down) and update parent:favorite relation

pygorithm/data_structures/linked_list.py

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55

66
class Node(object):
77
# Each node has its data and a pointer that points to next node in the Linked List
8-
def __init__(self, data, next = None):
8+
def __init__(self, data, next=None):
99
''' constructor '''
1010
self.data = data
1111
self.next = next
1212

1313
# easily retrieve the source code of the Node class
14-
def get_code(self):
14+
@classmethod
15+
def get_code(cls):
1516
''' return the code for the current class '''
1617
import inspect
17-
return inspect.getsource(Node)
18+
return inspect.getsource(cls)
19+
1820

1921
class SinglyLinkedList(object):
2022
# Defining the head of the linked list
@@ -34,7 +36,7 @@ def get_data(self):
3436
''' prints the elements in the linked list '''
3537
temp = self.head
3638
List = []
37-
while(temp):
39+
while (temp):
3840
# print(temp.data, end=' ')
3941
List.append(temp.data)
4042
temp = temp.next
@@ -43,7 +45,7 @@ def get_data(self):
4345

4446
def insert_at_start(self, data):
4547
''' insert an item at the beginning of the linked list '''
46-
if self.head == None:
48+
if self.head is None:
4749
newNode = Node(data)
4850
self.head = newNode
4951
else:
@@ -62,25 +64,25 @@ def insert_at_end(self, data):
6264
''' insert an item at the end of the linked list '''
6365
newNode = Node(data)
6466
temp = self.head
65-
while(temp.next != None): # get last node
67+
while (temp.next != None): # get last node
6668
temp = temp.next
6769
temp.next = newNode
6870

6971
def delete(self, data):
7072
''' to delete specified element from the linked list '''
7173
temp = self.head
7274
# if data/key is found in head node itself
73-
if (temp is not None):
74-
if(temp.data == data):
75+
if temp is not None:
76+
if temp.data == data:
7577
self.head = temp.next
7678
temp = None
7779
return
7880
else:
7981
# else _search all the nodes
80-
while(temp.next != None):
81-
if(temp.data == data):
82+
while (temp.next != None):
83+
if (temp.data == data):
8284
break
83-
prev = temp #save current node as previous so that we can go on to next node
85+
prev = temp # save current node as previous so that we can go on to next node
8486
temp = temp.next
8587

8688
# node not found
@@ -91,11 +93,13 @@ def delete(self, data):
9193
return
9294

9395
# easily retrieve the source code of the SinglyLinkedList class
94-
def get_code(self):
96+
@staticmethod
97+
def get_code():
9598
''' return the code for the current class '''
9699
import inspect
97100
return inspect.getsource(SinglyLinkedList)
98101

102+
99103
class DoublyLinkedList(object):
100104
def __init__(self):
101105
''' constructor '''
@@ -105,7 +109,7 @@ def get_data(self):
105109
''' prints the elements in the linked list '''
106110
temp = self.head
107111
List = []
108-
while(temp):
112+
while (temp):
109113
# print(temp.data, end=' ')
110114
List.append(temp.data)
111115
temp = temp.next
@@ -127,27 +131,27 @@ def insert_at_end(self, data):
127131
''' insert an element at the end of the linked list '''
128132
newNode = Node(data)
129133
temp = self.head
130-
while(temp.next != None):
134+
while (temp.next != None):
131135
temp = temp.next
132136
temp.next = newNode
133137
newNode.previous = temp
134138

135139
def delete(self, data):
136140
''' to delete specified element from the linked list '''
137141
temp = self.head
138-
if(temp.next != None):
142+
if (temp.next != None):
139143
# if head node is to be deleted
140-
if(temp.data == data):
144+
if (temp.data == data):
141145
temp.next.previous = None
142146
self.head = temp.next
143147
temp.next = None
144148
return
145149
else:
146-
while(temp.next != None):
147-
if(temp.data == data):
150+
while (temp.next != None):
151+
if (temp.data == data):
148152
break
149153
temp = temp.next
150-
if(temp.next):
154+
if (temp.next):
151155
# if element to be deleted is in between
152156
temp.previous.next = temp.next
153157
temp.next.previous = temp.previous
@@ -159,11 +163,12 @@ def delete(self, data):
159163
temp.previous = None
160164
return
161165

162-
if (temp == None):
166+
if temp is None:
163167
return
164168

165169
# easily retrieve the source code of the DoublyLinkedList class
166-
def get_code(self):
170+
@staticmethod
171+
def get_code():
167172
''' returns the code of the current class '''
168173
import inspect
169174
return inspect.getsource(DoublyLinkedList)

pygorithm/data_structures/modules.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import pkgutil
2+
3+
24
def modules():
35
"""
46
Find all functions in pygorithm.data_structures
@@ -11,4 +13,3 @@ def modules():
1113
modules.remove('modules')
1214
modules.sort()
1315
return modules
14-

0 commit comments

Comments
 (0)