From 8cf8deb4bd0779abd12522580bb2a554b2b5302a Mon Sep 17 00:00:00 2001 From: Tapajyoti Bose Date: Tue, 21 Jul 2020 13:01:05 +0530 Subject: [PATCH 1/7] Added Kruskal's Algorithm --- graphs/minimum_spanning_tree_kruskal2.py | 108 +++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 graphs/minimum_spanning_tree_kruskal2.py diff --git a/graphs/minimum_spanning_tree_kruskal2.py b/graphs/minimum_spanning_tree_kruskal2.py new file mode 100644 index 000000000000..5e7175d2284b --- /dev/null +++ b/graphs/minimum_spanning_tree_kruskal2.py @@ -0,0 +1,108 @@ +class Disjoint_Set_Tree_Node: + # Disjoint Set Node to store the parent and rank + def __init__(self, key): + self.key = key + self.parent = self + self.rank = 0 + + +class Disjoint_Set_Tree: + # Disjoint Set DataStructure + def __init__(self): + # map from node name to the node object + self.map = {} + + def make_set(self, x): + # create a new set with x as its member + self.map[x] = Disjoint_Set_Tree_Node(x) + + def find_set(self, x): + # find the set x belongs to (with path-compression) + if self.map[x] != self.map[x].parent: + self.map[x].parent = self.find_set(self.map[x].parent.key) + return self.map[x].parent + + def link(self, x, y): + # helper function for union operation + if x.rank > y.rank: + y.parent = x + else: + x.parent = y + if x.rank == y.rank: + y.rank += 1 + + def union(self, x, y): + # merge 2 disjoint sets + self.link(self.find_set(x), self.find_set(y)) + + +class GraphUndirectedWeighted: + def __init__(self): + # connections: map from the node to the neighbouring nodes (with weights) + # nodes: counting the number of nodes in the graph + self.connections = {} + self.nodes = 0 + + def add_node(self, node): + # add a node ONLY if its not present in the graph + if node not in self.connections: + self.connections[node] = {} + self.nodes += 1 + + def add_edge(self, node1, node2, weight): + # add an edge with the given weight + self.add_node(node1) + self.add_node(node2) + self.connections[node1][node2] = weight + self.connections[node2][node1] = weight + + def kruskal(self): + # Kruskal's Algorithm to generate a Minimum Spanning Tree (MST) of a graph + """ + Details: https://en.wikipedia.org/wiki/Kruskal%27s_algorithm + + Example: + + >>> graph = GraphUndirectedWeighted() + >>> graph.add_edge(1, 2, 1) + >>> graph.add_edge(2, 3, 2) + >>> graph.add_edge(3, 4, 1) + >>> graph.add_edge(3, 5, 100) # Removed in MST + >>> graph.add_edge(4, 5, 5) + >>> assert 5 in graph.connections[3] + >>> mst = graph.kruskal() + >>> assert 5 not in mst.connections[3] + """ + + # getting the edges in ascending order of weights + edges = [] + seen = set() + for start in self.connections: + for end in self.connections[start]: + if (start, end) not in seen: + seen.add((end, start)) + edges.append((start, end, self.connections[start][end])) + edges.sort(key=lambda x: x[2]) + # creating the disjoint set + disjoint_set = Disjoint_Set_Tree() + [disjoint_set.make_set(node) for node in self.connections] + # MST generation + num_edges = 0 + index = 0 + graph = GraphUndirectedWeighted() + while num_edges < self.nodes - 1: + u, v, w = edges[index] + index += 1 + parentu = disjoint_set.find_set(u) + parentv = disjoint_set.find_set(v) + if parentu != parentv: + num_edges += 1 + graph.add_edge(u, v, w) + disjoint_set.union(u, v) + return graph + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 4d3617ee6581980f2b9f25e595b8df74b7c58856 Mon Sep 17 00:00:00 2001 From: Tapajyoti Bose Date: Tue, 21 Jul 2020 13:07:42 +0530 Subject: [PATCH 2/7] Added Type Hints --- graphs/minimum_spanning_tree_kruskal2.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/graphs/minimum_spanning_tree_kruskal2.py b/graphs/minimum_spanning_tree_kruskal2.py index 5e7175d2284b..5101af0c5c98 100644 --- a/graphs/minimum_spanning_tree_kruskal2.py +++ b/graphs/minimum_spanning_tree_kruskal2.py @@ -1,6 +1,9 @@ +from __future__ import annotations + + class Disjoint_Set_Tree_Node: # Disjoint Set Node to store the parent and rank - def __init__(self, key): + def __init__(self, key: int) -> None: self.key = key self.parent = self self.rank = 0 @@ -12,17 +15,17 @@ def __init__(self): # map from node name to the node object self.map = {} - def make_set(self, x): + def make_set(self, x) -> None: # create a new set with x as its member self.map[x] = Disjoint_Set_Tree_Node(x) - def find_set(self, x): + def find_set(self, x: int) -> Disjoint_Set_Tree_Node: # find the set x belongs to (with path-compression) if self.map[x] != self.map[x].parent: self.map[x].parent = self.find_set(self.map[x].parent.key) return self.map[x].parent - def link(self, x, y): + def link(self, x, y) -> None: # helper function for union operation if x.rank > y.rank: y.parent = x @@ -31,7 +34,7 @@ def link(self, x, y): if x.rank == y.rank: y.rank += 1 - def union(self, x, y): + def union(self, x, y) -> None: # merge 2 disjoint sets self.link(self.find_set(x), self.find_set(y)) @@ -43,20 +46,20 @@ def __init__(self): self.connections = {} self.nodes = 0 - def add_node(self, node): + def add_node(self, node: int) -> None: # add a node ONLY if its not present in the graph if node not in self.connections: self.connections[node] = {} self.nodes += 1 - def add_edge(self, node1, node2, weight): + def add_edge(self, node1: int, node2: int, weight: int) -> None: # add an edge with the given weight self.add_node(node1) self.add_node(node2) self.connections[node1][node2] = weight self.connections[node2][node1] = weight - def kruskal(self): + def kruskal(self) -> GraphUndirectedWeighted: # Kruskal's Algorithm to generate a Minimum Spanning Tree (MST) of a graph """ Details: https://en.wikipedia.org/wiki/Kruskal%27s_algorithm From c381078c43b712275f44c0f227eeeea73db5c8e3 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Tue, 21 Jul 2020 07:38:53 +0000 Subject: [PATCH 3/7] fixup! Format Python code with psf/black push --- conversions/temperature_conversions.py | 1 + linear_algebra/src/test_linear_algebra.py | 9 +-------- web_programming/daily_horoscope.py | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/conversions/temperature_conversions.py b/conversions/temperature_conversions.py index 6b99d7688e44..591322badc96 100644 --- a/conversions/temperature_conversions.py +++ b/conversions/temperature_conversions.py @@ -85,4 +85,5 @@ def kelvin_to_celsius(kelvin: float) -> float: if __name__ == "__main__": import doctest + doctest.testmod() diff --git a/linear_algebra/src/test_linear_algebra.py b/linear_algebra/src/test_linear_algebra.py index be6245747f87..8db480ceb29d 100644 --- a/linear_algebra/src/test_linear_algebra.py +++ b/linear_algebra/src/test_linear_algebra.py @@ -8,14 +8,7 @@ """ import unittest -from lib import ( - Matrix, - Vector, - axpy, - squareZeroMatrix, - unitBasisVector, - zeroVector, -) +from lib import Matrix, Vector, axpy, squareZeroMatrix, unitBasisVector, zeroVector class Test(unittest.TestCase): diff --git a/web_programming/daily_horoscope.py b/web_programming/daily_horoscope.py index ecb37ce106f4..b0dd1cd65924 100644 --- a/web_programming/daily_horoscope.py +++ b/web_programming/daily_horoscope.py @@ -1,5 +1,5 @@ -from bs4 import BeautifulSoup import requests +from bs4 import BeautifulSoup def horoscope(zodiac_sign: int, day: str) -> str: From a3b01c669a2a700fdc5a8cb1a927af0328c98e02 Mon Sep 17 00:00:00 2001 From: Tapajyoti Bose Date: Tue, 21 Jul 2020 13:49:30 +0530 Subject: [PATCH 4/7] Added Type Hints V2 --- graphs/minimum_spanning_tree_kruskal2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graphs/minimum_spanning_tree_kruskal2.py b/graphs/minimum_spanning_tree_kruskal2.py index 5101af0c5c98..9edf8ea3ac88 100644 --- a/graphs/minimum_spanning_tree_kruskal2.py +++ b/graphs/minimum_spanning_tree_kruskal2.py @@ -15,7 +15,7 @@ def __init__(self): # map from node name to the node object self.map = {} - def make_set(self, x) -> None: + def make_set(self, x: int) -> None: # create a new set with x as its member self.map[x] = Disjoint_Set_Tree_Node(x) @@ -25,7 +25,7 @@ def find_set(self, x: int) -> Disjoint_Set_Tree_Node: self.map[x].parent = self.find_set(self.map[x].parent.key) return self.map[x].parent - def link(self, x, y) -> None: + def link(self, x: int, y: int) -> None: # helper function for union operation if x.rank > y.rank: y.parent = x @@ -34,7 +34,7 @@ def link(self, x, y) -> None: if x.rank == y.rank: y.rank += 1 - def union(self, x, y) -> None: + def union(self, x: int, y: int) -> None: # merge 2 disjoint sets self.link(self.find_set(x), self.find_set(y)) From 3073d08be3148885f74f347fb7f800f51f8c6aff Mon Sep 17 00:00:00 2001 From: Tapajyoti Bose Date: Wed, 12 Aug 2020 08:26:18 +0530 Subject: [PATCH 5/7] Implemented suggestions + uniform naming convention --- graphs/minimum_spanning_tree_kruskal2.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/graphs/minimum_spanning_tree_kruskal2.py b/graphs/minimum_spanning_tree_kruskal2.py index 9edf8ea3ac88..72a746e09438 100644 --- a/graphs/minimum_spanning_tree_kruskal2.py +++ b/graphs/minimum_spanning_tree_kruskal2.py @@ -1,7 +1,7 @@ from __future__ import annotations -class Disjoint_Set_Tree_Node: +class DisjointSetTreeNode: # Disjoint Set Node to store the parent and rank def __init__(self, key: int) -> None: self.key = key @@ -9,7 +9,7 @@ def __init__(self, key: int) -> None: self.rank = 0 -class Disjoint_Set_Tree: +class DisjointSetTree: # Disjoint Set DataStructure def __init__(self): # map from node name to the node object @@ -17,13 +17,14 @@ def __init__(self): def make_set(self, x: int) -> None: # create a new set with x as its member - self.map[x] = Disjoint_Set_Tree_Node(x) + self.map[x] = DisjointSetTreeNode(x) - def find_set(self, x: int) -> Disjoint_Set_Tree_Node: + def find_set(self, x: int) -> DisjointSetTreeNode: # find the set x belongs to (with path-compression) - if self.map[x] != self.map[x].parent: - self.map[x].parent = self.find_set(self.map[x].parent.key) - return self.map[x].parent + elem_ref = self.map[x] + if elem_ref != elem_ref.parent: + elem_ref.parent = self.find_set(elem_ref.parent.key) + return elem_ref.parent def link(self, x: int, y: int) -> None: # helper function for union operation @@ -87,7 +88,7 @@ def kruskal(self) -> GraphUndirectedWeighted: edges.append((start, end, self.connections[start][end])) edges.sort(key=lambda x: x[2]) # creating the disjoint set - disjoint_set = Disjoint_Set_Tree() + disjoint_set = DisjointSetTree() [disjoint_set.make_set(node) for node in self.connections] # MST generation num_edges = 0 From 05e92efbbec562aff4a278810e8340db5ef5f97a Mon Sep 17 00:00:00 2001 From: Tapajyoti Bose Date: Wed, 12 Aug 2020 12:05:35 +0530 Subject: [PATCH 6/7] removed redundant variable (self.nodes) --- graphs/minimum_spanning_tree_kruskal2.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/graphs/minimum_spanning_tree_kruskal2.py b/graphs/minimum_spanning_tree_kruskal2.py index 72a746e09438..dfb87efeb89a 100644 --- a/graphs/minimum_spanning_tree_kruskal2.py +++ b/graphs/minimum_spanning_tree_kruskal2.py @@ -43,15 +43,12 @@ def union(self, x: int, y: int) -> None: class GraphUndirectedWeighted: def __init__(self): # connections: map from the node to the neighbouring nodes (with weights) - # nodes: counting the number of nodes in the graph self.connections = {} - self.nodes = 0 def add_node(self, node: int) -> None: # add a node ONLY if its not present in the graph if node not in self.connections: self.connections[node] = {} - self.nodes += 1 def add_edge(self, node1: int, node2: int, weight: int) -> None: # add an edge with the given weight @@ -94,7 +91,7 @@ def kruskal(self) -> GraphUndirectedWeighted: num_edges = 0 index = 0 graph = GraphUndirectedWeighted() - while num_edges < self.nodes - 1: + while num_edges < len(self.connections) - 1: u, v, w = edges[index] index += 1 parentu = disjoint_set.find_set(u) From 44ff6caaecc8a3f3cf8f9d48682ac1194eadc112 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Wed, 12 Aug 2020 06:36:56 +0000 Subject: [PATCH 7/7] updating DIRECTORY.md --- DIRECTORY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index fb8312c635f8..2b4e1728266b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -86,6 +86,7 @@ * [Decimal To Hexadecimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_hexadecimal.py) * [Decimal To Octal](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_octal.py) * [Roman To Integer](https://github.com/TheAlgorithms/Python/blob/master/conversions/roman_to_integer.py) + * [Temperature Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/temperature_conversions.py) ## Data Structures * Binary Tree @@ -266,6 +267,7 @@ * [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 Kruskal2](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_kruskal2.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) * [Page Rank](https://github.com/TheAlgorithms/Python/blob/master/graphs/page_rank.py) @@ -337,6 +339,7 @@ * [Binary Exp Mod](https://github.com/TheAlgorithms/Python/blob/master/maths/binary_exp_mod.py) * [Binary Exponentiation](https://github.com/TheAlgorithms/Python/blob/master/maths/binary_exponentiation.py) * [Binomial Coefficient](https://github.com/TheAlgorithms/Python/blob/master/maths/binomial_coefficient.py) + * [Binomial Distribution](https://github.com/TheAlgorithms/Python/blob/master/maths/binomial_distribution.py) * [Bisection](https://github.com/TheAlgorithms/Python/blob/master/maths/bisection.py) * [Ceil](https://github.com/TheAlgorithms/Python/blob/master/maths/ceil.py) * [Chudnovsky Algorithm](https://github.com/TheAlgorithms/Python/blob/master/maths/chudnovsky_algorithm.py) @@ -633,6 +636,7 @@ * [I Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/i_sort.py) * [Insertion Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/insertion_sort.py) * [Iterative Merge Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/iterative_merge_sort.py) + * [Merge Insertion Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/merge_insertion_sort.py) * [Merge Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/merge_sort.py) * [Odd Even Transposition Parallel](https://github.com/TheAlgorithms/Python/blob/master/sorts/odd_even_transposition_parallel.py) * [Odd Even Transposition Single Threaded](https://github.com/TheAlgorithms/Python/blob/master/sorts/odd_even_transposition_single_threaded.py)