From d65aecf4ec52ddb43f931dc9585d5aa133ada817 Mon Sep 17 00:00:00 2001 From: Manik Date: Mon, 14 Oct 2024 15:19:12 +0530 Subject: [PATCH 1/8] Added code for quicksort.py and kruskal algorithm --- divide_and_conquer/quicksort.py | 46 +++++++++++++++++ graphs/kruskal.py | 90 +++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 divide_and_conquer/quicksort.py create mode 100644 graphs/kruskal.py diff --git a/divide_and_conquer/quicksort.py b/divide_and_conquer/quicksort.py new file mode 100644 index 000000000000..16509c34ea77 --- /dev/null +++ b/divide_and_conquer/quicksort.py @@ -0,0 +1,46 @@ +# Function to perform partition of the array +def partition(arr, low, high): + # Choose the last element as the pivot + pivot = arr[high] + + # Pointer for greater element + i = low - 1 # index of smaller element + + # Traverse through all elements + for j in range(low, high): + # If the current element is smaller than or equal to the pivot + if arr[j] <= pivot: + i = i + 1 # Increment the index of smaller element + arr[i], arr[j] = arr[j], arr[i] # Swap + + # Swap the pivot element with the element at i+1 + arr[i + 1], arr[high] = arr[high], arr[i + 1] + + # Return the partition point + return i + 1 + +# Function to implement Quick Sort +def quick_sort(arr, low, high): + if low < high: + # Find the partition index + pi = partition(arr, low, high) + + # Recursively sort the elements before and after partition + quick_sort(arr, low, pi - 1) # Before partition + quick_sort(arr, pi + 1, high) # After partition + +# Driver code to take user-defined input and sort +if __name__ == "__main__": + # Ask the user for input + n = int(input("Enter the number of elements in the array: ")) + + # Input array elements from the user + arr = list(map(int, input(f"Enter {n} elements separated by spaces: ").split())) + + print("Original array:", arr) + + # Call quick sort function + quick_sort(arr, 0, len(arr) - 1) + + # Print sorted array + print("Sorted array:", arr) diff --git a/graphs/kruskal.py b/graphs/kruskal.py new file mode 100644 index 000000000000..6216c7f26484 --- /dev/null +++ b/graphs/kruskal.py @@ -0,0 +1,90 @@ +# Class to represent a graph +class Graph: + def __init__(self, vertices): + self.V = vertices # Number of vertices + self.graph = [] # List to store graph edges (u, v, w) + + # Function to add an edge to the graph (u -> v with weight w) + def add_edge(self, u, v, w): + self.graph.append([u, v, w]) + + # Utility function to find set of an element i (uses path compression) + def find(self, parent, i): + if parent[i] == i: + return i + return self.find(parent, parent[i]) + + # Function that does union of two sets of x and y (uses union by rank) + def union(self, parent, rank, x, y): + root_x = self.find(parent, x) + root_y = self.find(parent, y) + + # Attach smaller rank tree under the root of the high-rank tree + if rank[root_x] < rank[root_y]: + parent[root_x] = root_y + elif rank[root_x] > rank[root_y]: + parent[root_y] = root_x + else: + parent[root_y] = root_x + rank[root_x] += 1 + + # Main function to construct MST using Kruskal's algorithm + def kruskal_mst(self): + # This will store the resultant Minimum Spanning Tree (MST) + result = [] + + # Step 1: Sort all edges in non-decreasing order of their weight + # If we are using a greedy algorithm, we need to sort the edges first + self.graph = sorted(self.graph, key=lambda item: item[2]) + + # Allocate memory for creating V subsets (for the disjoint-set) + parent = [] + rank = [] + + # Create V subsets with single elements + for node in range(self.V): + parent.append(node) + rank.append(0) + + # Number of edges in MST is V-1, so we will stop once we have V-1 edges + e = 0 # Initialize result edges count + i = 0 # Initialize the index for sorted edges + + # Loop until MST has V-1 edges + while e < self.V - 1: + # Step 2: Pick the smallest edge and increment the index for the next iteration + u, v, w = self.graph[i] + i = i + 1 + + # Step 3: Find sets of both vertices u and v (to check if adding this edge will form a cycle) + x = self.find(parent, u) + y = self.find(parent, v) + + # If adding this edge doesn't cause a cycle, include it in the result + if x != y: + result.append([u, v, w]) + e = e + 1 # Increment the count of edges in the MST + self.union(parent, rank, x, y) + + # Else, discard the edge (it would create a cycle) + + # Print the constructed Minimum Spanning Tree + print("Following are the edges in the constructed MST:") + for u, v, w in result: + print(f"{u} -- {v} == {w}") + +# Example usage +if __name__ == "__main__": + V = int(input("Enter the number of vertices: ")) # Ask user for the number of vertices + E = int(input("Enter the number of edges: ")) # Ask user for the number of edges + + g = Graph(V) # Create a graph with V vertices + + # Ask user to input the edges in the format u v w + print("Enter each edge in the format: vertex1 vertex2 weight") + for _ in range(E): + u, v, w = map(int, input().split()) # Take input for edge (u, v) with weight w + g.add_edge(u, v, w) + + # Print the constructed MST + g.kruskal_mst() From 7eb698480a66f1ecde6a5036ac82d27f0f3bf606 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 09:54:08 +0000 Subject: [PATCH 2/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- divide_and_conquer/quicksort.py | 2 ++ graphs/kruskal.py | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/divide_and_conquer/quicksort.py b/divide_and_conquer/quicksort.py index 16509c34ea77..890917461230 100644 --- a/divide_and_conquer/quicksort.py +++ b/divide_and_conquer/quicksort.py @@ -19,6 +19,7 @@ def partition(arr, low, high): # Return the partition point return i + 1 + # Function to implement Quick Sort def quick_sort(arr, low, high): if low < high: @@ -29,6 +30,7 @@ def quick_sort(arr, low, high): quick_sort(arr, low, pi - 1) # Before partition quick_sort(arr, pi + 1, high) # After partition + # Driver code to take user-defined input and sort if __name__ == "__main__": # Ask the user for input diff --git a/graphs/kruskal.py b/graphs/kruskal.py index 6216c7f26484..c0da7438522b 100644 --- a/graphs/kruskal.py +++ b/graphs/kruskal.py @@ -2,7 +2,7 @@ class Graph: def __init__(self, vertices): self.V = vertices # Number of vertices - self.graph = [] # List to store graph edges (u, v, w) + self.graph = [] # List to store graph edges (u, v, w) # Function to add an edge to the graph (u -> v with weight w) def add_edge(self, u, v, w): @@ -73,10 +73,13 @@ def kruskal_mst(self): for u, v, w in result: print(f"{u} -- {v} == {w}") + # Example usage if __name__ == "__main__": - V = int(input("Enter the number of vertices: ")) # Ask user for the number of vertices - E = int(input("Enter the number of edges: ")) # Ask user for the number of edges + V = int( + input("Enter the number of vertices: ") + ) # Ask user for the number of vertices + E = int(input("Enter the number of edges: ")) # Ask user for the number of edges g = Graph(V) # Create a graph with V vertices From 24bcc2edc24d75b9702dc55b94376860165b913e Mon Sep 17 00:00:00 2001 From: Manik Chauhan Date: Mon, 14 Oct 2024 15:31:09 +0530 Subject: [PATCH 3/8] Update kruskal.py --- graphs/kruskal.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/graphs/kruskal.py b/graphs/kruskal.py index c0da7438522b..a98fb7267f87 100644 --- a/graphs/kruskal.py +++ b/graphs/kruskal.py @@ -52,11 +52,12 @@ def kruskal_mst(self): # Loop until MST has V-1 edges while e < self.V - 1: - # Step 2: Pick the smallest edge and increment the index for the next iteration + # Step 2: Pick the smallest edge + #increment the index for the next iteration u, v, w = self.graph[i] i = i + 1 - # Step 3: Find sets of both vertices u and v (to check if adding this edge will form a cycle) + # Step 3: Find sets of both vertices u and v x = self.find(parent, u) y = self.find(parent, v) From f0f2213e3e61be0d82aeba607d700c5f0ba34f10 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:01:33 +0000 Subject: [PATCH 4/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- graphs/kruskal.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graphs/kruskal.py b/graphs/kruskal.py index a98fb7267f87..1dfbd8246873 100644 --- a/graphs/kruskal.py +++ b/graphs/kruskal.py @@ -52,12 +52,12 @@ def kruskal_mst(self): # Loop until MST has V-1 edges while e < self.V - 1: - # Step 2: Pick the smallest edge - #increment the index for the next iteration + # Step 2: Pick the smallest edge + # increment the index for the next iteration u, v, w = self.graph[i] i = i + 1 - # Step 3: Find sets of both vertices u and v + # Step 3: Find sets of both vertices u and v x = self.find(parent, u) y = self.find(parent, v) From d3402ee133a17d4dcbf415b65b7ff395911cf381 Mon Sep 17 00:00:00 2001 From: Manik Date: Mon, 14 Oct 2024 15:44:22 +0530 Subject: [PATCH 5/8] updated requirements to solve issue --- divide_and_conquer/quicksort.py | 69 +++++++---- graphs/kruskal.py | 195 +++++++++++++++++++++----------- 2 files changed, 176 insertions(+), 88 deletions(-) diff --git a/divide_and_conquer/quicksort.py b/divide_and_conquer/quicksort.py index 890917461230..d5e0be96ed10 100644 --- a/divide_and_conquer/quicksort.py +++ b/divide_and_conquer/quicksort.py @@ -1,35 +1,60 @@ -# Function to perform partition of the array -def partition(arr, low, high): - # Choose the last element as the pivot - pivot = arr[high] +from typing import List - # Pointer for greater element - i = low - 1 # index of smaller element +# Function to implement Quick Sort +def quick_sort(arr: List[int], low: int, high: int) -> None: + """ + Perform quick sort on the given array in-place. - # Traverse through all elements - for j in range(low, high): - # If the current element is smaller than or equal to the pivot - if arr[j] <= pivot: - i = i + 1 # Increment the index of smaller element - arr[i], arr[j] = arr[j], arr[i] # Swap + Parameters: + arr (List[int]): The list of integers to sort. + low (int): The starting index of the portion of the array to sort. + high (int): The ending index of the portion of the array to sort. - # Swap the pivot element with the element at i+1 - arr[i + 1], arr[high] = arr[high], arr[i + 1] + Returns: + None: The function sorts the array in-place. - # Return the partition point - return i + 1 + Doctest: + >>> arr = [10, 7, 8, 9, 1, 5] + >>> quick_sort(arr, 0, len(arr) - 1) + >>> arr + [1, 5, 7, 8, 9, 10] + >>> arr = [4, 3, 2, 1] + >>> quick_sort(arr, 0, len(arr) - 1) + >>> arr + [1, 2, 3, 4] + """ -# Function to implement Quick Sort -def quick_sort(arr, low, high): if low < high: - # Find the partition index + # Partitioning index pi = partition(arr, low, high) - # Recursively sort the elements before and after partition - quick_sort(arr, low, pi - 1) # Before partition - quick_sort(arr, pi + 1, high) # After partition + # Recursively sort elements before and after partition + quick_sort(arr, low, pi - 1) + quick_sort(arr, pi + 1, high) + +def partition(arr: List[int], low: int, high: int) -> int: + """ + Partition function to place the pivot element at its correct position. + Parameters: + arr (List[int]): The list of integers to partition. + low (int): The starting index for the partition. + high (int): The ending index for the partition. + + Returns: + int: The partitioning index. + """ + pivot = arr[high] # Pivot + i = low - 1 # Index of smaller element + + for j in range(low, high): + if arr[j] <= pivot: + i += 1 + arr[i], arr[j] = arr[j], arr[i] # Swap + + arr[i + 1], arr[high] = arr[high], arr[i + 1] # Swap pivot + return i + 1 # Driver code to take user-defined input and sort if __name__ == "__main__": diff --git a/graphs/kruskal.py b/graphs/kruskal.py index c0da7438522b..dfc9727e697e 100644 --- a/graphs/kruskal.py +++ b/graphs/kruskal.py @@ -1,85 +1,145 @@ # Class to represent a graph class Graph: - def __init__(self, vertices): - self.V = vertices # Number of vertices + def __init__(self, vertices: int) -> None: + """ + Initialize the graph with the given number of vertices. + + Parameters: + vertices (int): The number of vertices in the graph. + + Returns: + None + """ + self.vertices = vertices self.graph = [] # List to store graph edges (u, v, w) # Function to add an edge to the graph (u -> v with weight w) - def add_edge(self, u, v, w): - self.graph.append([u, v, w]) - - # Utility function to find set of an element i (uses path compression) - def find(self, parent, i): - if parent[i] == i: - return i - return self.find(parent, parent[i]) + def add_edge(self, from_vertex: int, to_vertex: int, weight: float) -> None: + """ + Add an edge to the graph. + + Parameters: + from_vertex (int): The starting vertex of the edge. + to_vertex (int): The ending vertex of the edge. + weight (float): The weight of the edge. + + Returns: + None + + Doctest: + >>> g = Graph(3) + >>> g.add_edge(0, 1, 5.0) + >>> g.add_edge(1, 2, 3.5) + >>> g.graph + [(0, 1, 5.0), (1, 2, 3.5)] + """ + self.graph.append((from_vertex, to_vertex, weight)) + + # Utility function to find set of an element index (uses path compression) + def find(self, parent: list, vertex_index: int) -> int: + """ + Find the set of an element (vertex) using path compression. + + Parameters: + parent (list): The list representing the parent of each vertex. + vertex_index (int): The index of the vertex whose set is to be found. + + Returns: + int: The representative (root) of the set containing the vertex. + + Doctest: + >>> parent = [-1, 0, 0, 1] + >>> g = Graph(4) + >>> g.find(parent, 3) + 0 + >>> g.find(parent, 1) + 0 + """ + if parent[vertex_index] == -1: + return vertex_index + # Path compression + parent[vertex_index] = self.find(parent, parent[vertex_index]) + return parent[vertex_index] # Function that does union of two sets of x and y (uses union by rank) - def union(self, parent, rank, x, y): - root_x = self.find(parent, x) - root_y = self.find(parent, y) - - # Attach smaller rank tree under the root of the high-rank tree - if rank[root_x] < rank[root_y]: - parent[root_x] = root_y - elif rank[root_x] > rank[root_y]: - parent[root_y] = root_x - else: - parent[root_y] = root_x - rank[root_x] += 1 + def union(self, parent: list, rank: list, set_a: int, set_b: int) -> None: + """ + Union of two sets of set_a and set_b (uses union by rank). + + Parameters: + parent (list): The list representing the parent of each vertex. + rank (list): The list representing the rank of each vertex. + set_a (int): The representative of the first set. + set_b (int): The representative of the second set. + + Returns: + None + + Doctest: + >>> parent = [0, 0, -1, -1] + >>> rank = [1, 0, 0, 0] + >>> g = Graph(4) + >>> g.union(parent, rank, 0, 1) + >>> parent + [0, 0, -1, -1] + >>> g.union(parent, rank, 2, 3) + >>> parent + [0, 0, 2, 2] + """ + root_a = self.find(parent, set_a) + root_b = self.find(parent, set_b) + + if root_a != root_b: + if rank[root_a] < rank[root_b]: + parent[root_a] = root_b + elif rank[root_a] > rank[root_b]: + parent[root_b] = root_a + else: + parent[root_b] = root_a + rank[root_a] += 1 # Main function to construct MST using Kruskal's algorithm - def kruskal_mst(self): - # This will store the resultant Minimum Spanning Tree (MST) - result = [] - - # Step 1: Sort all edges in non-decreasing order of their weight - # If we are using a greedy algorithm, we need to sort the edges first + def kruskal_mst(self) -> list: + """ + Construct Minimum Spanning Tree (MST) using Kruskal's algorithm. + + Returns: + list: A list of edges included in the MST. + + Doctest: + >>> g = Graph(4) + >>> 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) + >>> mst_edges = g.kruskal_mst() + >>> mst_edges + [(0, 3, 5), (2, 3, 4), (0, 1, 10)] + """ + # Sort the edges based on their weights self.graph = sorted(self.graph, key=lambda item: item[2]) + parent = [-1] * self.vertices + rank = [0] * self.vertices + mst_edges = [] - # Allocate memory for creating V subsets (for the disjoint-set) - parent = [] - rank = [] - - # Create V subsets with single elements - for node in range(self.V): - parent.append(node) - rank.append(0) - - # Number of edges in MST is V-1, so we will stop once we have V-1 edges - e = 0 # Initialize result edges count - i = 0 # Initialize the index for sorted edges - - # Loop until MST has V-1 edges - while e < self.V - 1: - # Step 2: Pick the smallest edge and increment the index for the next iteration - u, v, w = self.graph[i] - i = i + 1 - - # Step 3: Find sets of both vertices u and v (to check if adding this edge will form a cycle) - x = self.find(parent, u) - y = self.find(parent, v) - - # If adding this edge doesn't cause a cycle, include it in the result - if x != y: - result.append([u, v, w]) - e = e + 1 # Increment the count of edges in the MST - self.union(parent, rank, x, y) + for edge in self.graph: + u, v, w = edge + root_u = self.find(parent, u) + root_v = self.find(parent, v) - # Else, discard the edge (it would create a cycle) + # If including this edge does not cause a cycle + if root_u != root_v: + mst_edges.append((u, v, w)) + self.union(parent, rank, root_u, root_v) - # Print the constructed Minimum Spanning Tree - print("Following are the edges in the constructed MST:") - for u, v, w in result: - print(f"{u} -- {v} == {w}") + return mst_edges # Example usage if __name__ == "__main__": - V = int( - input("Enter the number of vertices: ") - ) # Ask user for the number of vertices - E = int(input("Enter the number of edges: ")) # Ask user for the number of edges + V = int(input("Enter the number of vertices: ")) # Ask user for the number of vertices + E = int(input("Enter the number of edges: ")) # Ask user for the number of edges g = Graph(V) # Create a graph with V vertices @@ -90,4 +150,7 @@ def kruskal_mst(self): g.add_edge(u, v, w) # Print the constructed MST - g.kruskal_mst() + mst_result = g.kruskal_mst() + print("Minimum Spanning Tree (MST) edges:") + for edge in mst_result: + print(edge) From 8e56553d673a48cc26ff584071f0c6b4a7552028 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:16:28 +0000 Subject: [PATCH 6/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- divide_and_conquer/quicksort.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/divide_and_conquer/quicksort.py b/divide_and_conquer/quicksort.py index d5e0be96ed10..c61781cd0b84 100644 --- a/divide_and_conquer/quicksort.py +++ b/divide_and_conquer/quicksort.py @@ -1,5 +1,6 @@ from typing import List + # Function to implement Quick Sort def quick_sort(arr: List[int], low: int, high: int) -> None: """ @@ -33,6 +34,7 @@ def quick_sort(arr: List[int], low: int, high: int) -> None: quick_sort(arr, low, pi - 1) quick_sort(arr, pi + 1, high) + def partition(arr: List[int], low: int, high: int) -> int: """ Partition function to place the pivot element at its correct position. @@ -56,6 +58,7 @@ def partition(arr: List[int], low: int, high: int) -> int: arr[i + 1], arr[high] = arr[high], arr[i + 1] # Swap pivot return i + 1 + # Driver code to take user-defined input and sort if __name__ == "__main__": # Ask the user for input From b492d5ff39f09525bf5ad06fe56c6d8b4ed0e99e Mon Sep 17 00:00:00 2001 From: Manik Date: Mon, 14 Oct 2024 15:59:43 +0530 Subject: [PATCH 7/8] isssue resolved --- divide_and_conquer/quicksort.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/divide_and_conquer/quicksort.py b/divide_and_conquer/quicksort.py index c61781cd0b84..5fe821fda1fa 100644 --- a/divide_and_conquer/quicksort.py +++ b/divide_and_conquer/quicksort.py @@ -1,13 +1,10 @@ -from typing import List - - # Function to implement Quick Sort -def quick_sort(arr: List[int], low: int, high: int) -> None: +def quick_sort(arr: list[int], low: int, high: int) -> None: """ Perform quick sort on the given array in-place. Parameters: - arr (List[int]): The list of integers to sort. + arr (list[int]): The list of integers to sort. low (int): The starting index of the portion of the array to sort. high (int): The ending index of the portion of the array to sort. @@ -34,13 +31,12 @@ def quick_sort(arr: List[int], low: int, high: int) -> None: quick_sort(arr, low, pi - 1) quick_sort(arr, pi + 1, high) - -def partition(arr: List[int], low: int, high: int) -> int: +def partition(arr: list[int], low: int, high: int) -> int: """ Partition function to place the pivot element at its correct position. Parameters: - arr (List[int]): The list of integers to partition. + arr (list[int]): The list of integers to partition. low (int): The starting index for the partition. high (int): The ending index for the partition. @@ -58,7 +54,6 @@ def partition(arr: List[int], low: int, high: int) -> int: arr[i + 1], arr[high] = arr[high], arr[i + 1] # Swap pivot return i + 1 - # Driver code to take user-defined input and sort if __name__ == "__main__": # Ask the user for input From 9e8d831044287c4b099231bd5b8193329fee26fd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:30:13 +0000 Subject: [PATCH 8/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- divide_and_conquer/quicksort.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/divide_and_conquer/quicksort.py b/divide_and_conquer/quicksort.py index 5fe821fda1fa..ff54b8fb0007 100644 --- a/divide_and_conquer/quicksort.py +++ b/divide_and_conquer/quicksort.py @@ -31,6 +31,7 @@ def quick_sort(arr: list[int], low: int, high: int) -> None: quick_sort(arr, low, pi - 1) quick_sort(arr, pi + 1, high) + def partition(arr: list[int], low: int, high: int) -> int: """ Partition function to place the pivot element at its correct position. @@ -54,6 +55,7 @@ def partition(arr: list[int], low: int, high: int) -> int: arr[i + 1], arr[high] = arr[high], arr[i + 1] # Swap pivot return i + 1 + # Driver code to take user-defined input and sort if __name__ == "__main__": # Ask the user for input