From ee814525f1468e1b5a7733c76b00b3b1c1cf84b0 Mon Sep 17 00:00:00 2001 From: Adam Ross <14985050+R055A@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:44:56 +0200 Subject: [PATCH 1/7] Increase code coverage for dijkstra algorithm --- graphs/dijkstra_algorithm.py | 575 ++++++++++++++++++++++++++--------- 1 file changed, 430 insertions(+), 145 deletions(-) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index 452138fe904b..e8a3995ca443 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -1,211 +1,496 @@ -# Title: Dijkstra's Algorithm for finding single source shortest path from scratch +# Title: Dijkstra's Algorithm for finding the single-source shortest path in a graph # Author: Shubham Malik # References: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm import math import sys - -# For storing the vertex set to retrieve node with the lowest distance +from collections import defaultdict class PriorityQueue: - # Based on Min Heap - def __init__(self): + """ + Priority queue class. + For storing a vertex set to retrieve node with the lowest distance. + Based on Min Heap. + """ + + def __init__(self) -> None: + """ + Class constructor method. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.cur_size + 0 + >>> priority_queue_test.array + [] + >>> priority_queue_test.node_positions + {} + """ self.cur_size = 0 self.array = [] - self.pos = {} # To store the pos of node in array + self.node_positions = {} # To store the pos of node in array - def is_empty(self): + def is_empty(self) -> bool: + """ + Conditional boolean method to determine if the priority queue is empty or not. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.is_empty() + True + >>> priority_queue_test.insert((2, 'A')) + >>> priority_queue_test.is_empty() + False + """ return self.cur_size == 0 - def min_heapify(self, idx): - lc = self.left(idx) - rc = self.right(idx) - if lc < self.cur_size and self.array(lc)[0] < self.array(idx)[0]: - smallest = lc - else: - smallest = idx - if rc < self.cur_size and self.array(rc)[0] < self.array(smallest)[0]: - smallest = rc + def min_heapify(self, idx) -> None: + """ + Sorts the queue array so that the minimum element is root. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.cur_size = 3 + >>> priority_queue_test.node_positions = {'A': 0, 'B': 1, 'C': 2} + + >>> priority_queue_test.array = [(5, 'A'), (10, 'B'), (15, 'C')] + >>> priority_queue_test.min_heapify(0) + >>> priority_queue_test.array + [(5, 'A'), (10, 'B'), (15, 'C')] + + >>> priority_queue_test.array = [(10, 'A'), (5, 'B'), (15, 'C')] + >>> priority_queue_test.min_heapify(0) + >>> priority_queue_test.array + [(5, 'B'), (10, 'A'), (15, 'C')] + + >>> priority_queue_test.array = [(10, 'A'), (15, 'B'), (5, 'C')] + >>> priority_queue_test.min_heapify(0) + >>> priority_queue_test.array + [(5, 'C'), (15, 'B'), (10, 'A')] + + >>> priority_queue_test.array = [(10, 'A'), (5, 'B')] + >>> priority_queue_test.cur_size = len(priority_queue_test.array) + >>> priority_queue_test.node_positions = {'A': 0, 'B': 1} + >>> priority_queue_test.min_heapify(0) + >>> priority_queue_test.array + [(5, 'B'), (10, 'A')] + """ + left_child = self.left_child(idx) + right_child = self.right_child(idx) + + smallest = idx + + if ( + left_child < self.cur_size + and self.array[left_child][0] < self.array[idx][0] + ): + smallest = left_child + + if ( + right_child < self.cur_size + and self.array[right_child][0] < self.array[smallest][0] + ): + smallest = right_child + if smallest != idx: self.swap(idx, smallest) self.min_heapify(smallest) - def insert(self, tup): - # Inserts a node into the Priority Queue - self.pos[tup[1]] = self.cur_size + def insert(self, tup) -> None: + """ + Inserts a node into the Priority Queue. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.insert((10, 'A')) + >>> priority_queue_test.array + [(10, 'A')] + >>> priority_queue_test.insert((15, 'B')) + >>> priority_queue_test.array + [(10, 'A'), (15, 'B')] + >>> priority_queue_test.insert((5, 'C')) + >>> priority_queue_test.array + [(5, 'C'), (10, 'A'), (15, 'B')] + """ + self.node_positions[tup[1]] = self.cur_size self.cur_size += 1 self.array.append((sys.maxsize, tup[1])) self.decrease_key((sys.maxsize, tup[1]), tup[0]) - def extract_min(self): - # Removes and returns the min element at top of priority queue + def extract_min(self) -> str: + """ + Removes and returns the min element at top of priority queue. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] + >>> priority_queue_test.cur_size = len(priority_queue_test.array) + >>> priority_queue_test.node_positions = {'A': 0, 'B': 1} + >>> priority_queue_test.insert((5, 'C')) + >>> priority_queue_test.extract_min() + 'C' + >>> priority_queue_test.array[0] + (10, 'A') + """ min_node = self.array[0][1] self.array[0] = self.array[self.cur_size - 1] self.cur_size -= 1 - self.min_heapify(1) - del self.pos[min_node] + self.min_heapify(0) + del self.node_positions[min_node] return min_node - def left(self, i): - # returns the index of left child + @staticmethod + def left_child(i) -> int: + """ + Returns the index of left child + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.left_child(0) + 1 + >>> priority_queue_test.left_child(1) + 3 + """ return 2 * i + 1 - def right(self, i): - # returns the index of right child + @staticmethod + def right_child(i) -> int: + """ + Returns the index of right child + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.right_child(0) + 2 + >>> priority_queue_test.right_child(1) + 4 + """ return 2 * i + 2 - def par(self, i): - # returns the index of parent + @staticmethod + def parent_idx(i) -> int: + """ + Returns the index of parent + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.parent_idx(2) + 1 + >>> priority_queue_test.parent_idx(4) + 2 + """ return math.floor(i / 2) - def swap(self, i, j): - # swaps array elements at indices i and j - # update the pos{} - self.pos[self.array[i][1]] = j - self.pos[self.array[j][1]] = i + def swap(self, i, j) -> None: + """ + Swaps array elements at indices i and j, update the pos{} + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] + >>> priority_queue_test.cur_size = len(priority_queue_test.array) + >>> priority_queue_test.node_positions = {'A': 0, 'B': 1} + >>> priority_queue_test.swap(0, 1) + >>> priority_queue_test.array + [(15, 'B'), (10, 'A')] + >>> priority_queue_test.node_positions + {'A': 1, 'B': 0} + """ + self.node_positions[self.array[i][1]] = j + self.node_positions[self.array[j][1]] = i temp = self.array[i] self.array[i] = self.array[j] self.array[j] = temp - def decrease_key(self, tup, new_d): - idx = self.pos[tup[1]] - # assuming the new_d is atmost old_d + def decrease_key(self, tup, new_d) -> None: + """ + Decrease the key value for a given tuple, assuming the new_d is at most old_d. + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] + >>> priority_queue_test.cur_size = len(priority_queue_test.array) + >>> priority_queue_test.node_positions = {'A': 0, 'B': 1} + >>> priority_queue_test.decrease_key((10, 'A'), 5) + >>> priority_queue_test.array + [(5, 'A'), (15, 'B')] + """ + idx = self.node_positions[tup[1]] self.array[idx] = (new_d, tup[1]) - while idx > 0 and self.array[self.par(idx)][0] > self.array[idx][0]: - self.swap(idx, self.par(idx)) - idx = self.par(idx) + while idx > 0 and self.array[self.parent_idx(idx)][0] > self.array[idx][0]: + self.swap(idx, self.parent_idx(idx)) + idx = self.parent_idx(idx) class Graph: - def __init__(self, num): - self.adjList = {} # To store graph: u -> (v,w) - self.num_nodes = num # Number of nodes in graph - # To store the distance from source vertex - self.dist = [0] * self.num_nodes - self.par = [-1] * self.num_nodes # To store the path - - def add_edge(self, u, v, w): - # Edge going from node u to v and v to u with weight w - # u (w)-> v, v (w) -> u - # Check if u already in graph - if u in self.adjList: - self.adjList[u].append((v, w)) - else: - self.adjList[u] = [(v, w)] - - # Assuming undirected graph - if v in self.adjList: - self.adjList[v].append((u, w)) - else: - self.adjList[v] = [(u, w)] - - def show_graph(self): - # u -> v(w) - for u in self.adjList: - print(u, "->", " -> ".join(str(f"{v}({w})") for v, w in self.adjList[u])) - - def dijkstra(self, src): - # Flush old junk values in par[] - self.par = [-1] * self.num_nodes - # src is the source node - self.dist[src] = 0 - q = PriorityQueue() - q.insert((0, src)) # (dist from src, node) - for u in self.adjList: - if u != src: - self.dist[u] = sys.maxsize # Infinity - self.par[u] = -1 - - while not q.is_empty(): - u = q.extract_min() # Returns node with the min dist from source - # Update the distance of all the neighbours of u and - # if their prev dist was INFINITY then push them in Q - for v, w in self.adjList[u]: - new_dist = self.dist[u] + w - if self.dist[v] > new_dist: - if self.dist[v] == sys.maxsize: - q.insert((new_dist, v)) + """ + Graph class for computing Dijkstra's algorithm + """ + + def __init__(self, num_nodes) -> None: + """ + Class constructor + + Examples: + >>> graph_test = Graph(1) + >>> graph_test.num_nodes + 1 + >>> graph_test.dist_from_src + [0] + >>> graph_test.parent_idx + [-1] + >>> graph_test.get_adjacency_list() + {} + """ + self.adjacency_list = defaultdict(list) # To store graph: u -> (v,w) + self.num_nodes = num_nodes # Number of nodes in graph + self.dist_from_src = [ + 0 + ] * self.num_nodes # To store the distance from source vertex + self.parent_idx = [-1] * self.num_nodes # To store the path + + def get_adjacency_list(self) -> dict: + """ + Returns the defaultdict adjacency_list converted to dict() + """ + return dict(self.adjacency_list) + + def add_edge(self, u, v, w) -> None: + """ + Add edge going from node u to v and v to u with weight w: u (w)-> v, v (w) -> u + + Examples: + >>> graph_test = Graph(1) + >>> graph_test.add_edge(1, 2, 1) + >>> graph_test.add_edge(2, 3, 2) + >>> graph_test.get_adjacency_list() + {1: [(2, 1)], 2: [(1, 1), (3, 2)], 3: [(2, 2)]} + + >>> graph_test.add_edge(2, 4, -1) + Traceback (most recent call last): + ... + AssertionError: Dijkstra algorithm does not support negative edge weights! + """ + assert w >= 0, "Dijkstra algorithm does not support negative edge weights!" + self.adjacency_list[u].append((v, w)) + self.adjacency_list[v].append((u, w)) + + def show_graph(self) -> None: + """ + Show the graph: u -> v(w) + + Examples: + >>> graph_test = Graph(1) + >>> graph_test.add_edge(1, 2, 1) + >>> graph_test.show_graph() + 1 -> 2(1) + 2 -> 1(1) + >>> graph_test.add_edge(2, 3, 2) + >>> graph_test.show_graph() + 1 -> 2(1) + 2 -> 1(1) -> 3(2) + 3 -> 2(2) + """ + for u in self.adjacency_list: + print( + u, + "->", + " -> ".join(str(f"{v}({w})") for v, w in self.adjacency_list[u]), + ) + + def dijkstra(self, src, is_show_distance=True) -> None: + """ + Dijkstra algorithm + + Examples: + >>> graph_test = Graph(1) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + >>> graph_test.dist_from_src + [0] + + >>> graph_test = Graph(3) + >>> graph_test.add_edge(0, 1, 2) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 2 + Node 2 has distance: 4 + >>> graph_test.dist_from_src + [0, 2, 4] + + >>> graph_test = Graph(2) + >>> graph_test.add_edge(0, 1, 2) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 2 + >>> graph_test.dist_from_src + [0, 2] + + >>> graph_test = Graph(3) + >>> graph_test.add_edge(0, 1, 2) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 2 + Node 2 has distance: 9223372036854775807 + >>> graph_test.dist_from_src + [0, 2, 9223372036854775807] + + >>> graph_test = Graph(3) + >>> graph_test.add_edge(0, 1, 2) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.add_edge(0, 2, 1) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 2 + Node 2 has distance: 1 + >>> graph_test.dist_from_src + [0, 2, 1] + """ + self.dist_from_src = [ + sys.maxsize + ] * self.num_nodes # init with inf values for all node distances + self.parent_idx = [-1] * self.num_nodes # flush old junk values in par[] + self.dist_from_src[src] = 0 # src is the source node + + priority_queue = PriorityQueue() + priority_queue.insert((0, src)) # (dist from src, node) + priority_queue_vertex_tracker = set( + range(self.num_nodes) + ) # track all vertices initially + + while not priority_queue.is_empty(): + u = ( + priority_queue.extract_min() + ) # return node with the min dist from source + + if u in priority_queue_vertex_tracker: + priority_queue_vertex_tracker.remove( + u + ) # Remove u from tracker since it's extracted + + for v, w in self.adjacency_list[u]: + new_dist = self.dist_from_src[u] + w + + if self.dist_from_src[v] > new_dist: + self.dist_from_src[v] = new_dist + self.parent_idx[v] = u + + if ( + v in priority_queue_vertex_tracker + and v in priority_queue.node_positions + ): + priority_queue.decrease_key( + (self.dist_from_src[v], v), new_dist + ) + priority_queue_vertex_tracker.remove(v) else: - q.decrease_key((self.dist[v], v), new_dist) - self.dist[v] = new_dist - self.par[v] = u + priority_queue.insert((new_dist, v)) + + if is_show_distance: + self.show_distances(src) # Show the shortest distances from src - # Show the shortest distances from src - self.show_distances(src) + def show_distances(self, src) -> None: + """ + Show the distances from src to all other nodes in a graph - def show_distances(self, src): + Examples: + >>> graph_test = Graph(1) + >>> graph_test.show_distances(0) + Distance from node: 0 + Node 0 has distance: 0 + """ print(f"Distance from node: {src}") for u in range(self.num_nodes): - print(f"Node {u} has distance: {self.dist[u]}") + print(f"Node {u} has distance: {self.dist_from_src[u]}") def show_path(self, src, dest): - # To show the shortest path from src to dest - # WARNING: Use it *after* calling dijkstra + """ + Shows the shortest path from src to dest. + WARNING: Use it *after* calling dijkstra. + + Examples: + >>> graph_test = Graph(4) + >>> graph_test.add_edge(0, 1, 1) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.add_edge(2, 3, 3) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 1 + Node 2 has distance: 3 + Node 3 has distance: 6 + >>> graph_test.show_path(0, 3) + ----Path to reach 3 from 0---- + 0 -> 1 -> 2 -> 3 + Total cost of path: 6 + """ path = [] cost = 0 temp = dest # Backtracking from dest to src - while self.par[temp] != -1: + while self.parent_idx[temp] != -1: path.append(temp) if temp != src: - for v, w in self.adjList[temp]: - if v == self.par[temp]: + for v, w in self.adjacency_list[temp]: + if v == self.parent_idx[temp]: cost += w break - temp = self.par[temp] + temp = self.parent_idx[temp] path.append(src) path.reverse() print(f"----Path to reach {dest} from {src}----") for u in path: - print(f"{u}", end=" ") + print(f"{u}", end="") if u != dest: - print("-> ", end="") + print(" -> ", end="") print("\nTotal cost of path: ", cost) if __name__ == "__main__": - graph = Graph(9) - graph.add_edge(0, 1, 4) - graph.add_edge(0, 7, 8) - graph.add_edge(1, 2, 8) - graph.add_edge(1, 7, 11) - graph.add_edge(2, 3, 7) - graph.add_edge(2, 8, 2) - graph.add_edge(2, 5, 4) - graph.add_edge(3, 4, 9) - graph.add_edge(3, 5, 14) - graph.add_edge(4, 5, 10) - graph.add_edge(5, 6, 2) - graph.add_edge(6, 7, 1) - graph.add_edge(6, 8, 6) - graph.add_edge(7, 8, 7) - graph.show_graph() - graph.dijkstra(0) - graph.show_path(0, 4) - -# OUTPUT -# 0 -> 1(4) -> 7(8) -# 1 -> 0(4) -> 2(8) -> 7(11) -# 7 -> 0(8) -> 1(11) -> 6(1) -> 8(7) -# 2 -> 1(8) -> 3(7) -> 8(2) -> 5(4) -# 3 -> 2(7) -> 4(9) -> 5(14) -# 8 -> 2(2) -> 6(6) -> 7(7) -# 5 -> 2(4) -> 3(14) -> 4(10) -> 6(2) -# 4 -> 3(9) -> 5(10) -# 6 -> 5(2) -> 7(1) -> 8(6) -# Distance from node: 0 -# Node 0 has distance: 0 -# Node 1 has distance: 4 -# Node 2 has distance: 12 -# Node 3 has distance: 19 -# Node 4 has distance: 21 -# Node 5 has distance: 11 -# Node 6 has distance: 9 -# Node 7 has distance: 8 -# Node 8 has distance: 14 -# ----Path to reach 4 from 0---- -# 0 -> 7 -> 6 -> 5 -> 4 -# Total cost of path: 21 + from doctest import testmod + from timeit import timeit + + testmod() + + def init_test_graph(): + graph = Graph(9) + graph.add_edge(0, 1, 4) + graph.add_edge(0, 7, 8) + graph.add_edge(1, 2, 8) + graph.add_edge(1, 7, 11) + graph.add_edge(2, 3, 7) + graph.add_edge(2, 8, 2) + graph.add_edge(2, 5, 4) + graph.add_edge(3, 4, 9) + graph.add_edge(3, 5, 14) + graph.add_edge(4, 5, 10) + graph.add_edge(5, 6, 2) + graph.add_edge(6, 7, 1) + graph.add_edge(6, 8, 6) + graph.add_edge(7, 8, 7) + return graph + + print("\nGraph prior to Dijkstra algorithm sorting:") + test_graph = init_test_graph() + test_graph.show_graph() + + num_runs = 1000 + timer_dijkstra = timeit( + setup="graph = init_test_graph()", + stmt="graph.dijkstra(0, False)", + globals=globals(), + number=num_runs, + ) + print(f"\nDijkstra processing time: {timer_dijkstra:.5f} s for {num_runs} runs") + + print("\nGraph after Dijkstra algorithm sorting:") + test_graph.dijkstra(src=0, is_show_distance=False) + test_graph.show_graph() From 1ffa7a534eed0492af406d661d7ee25a1070bd67 Mon Sep 17 00:00:00 2001 From: Adam Ross <14985050+R055A@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:30:42 +0200 Subject: [PATCH 2/7] Add missing code coverage Refactor to pass mypy --- graphs/dijkstra_algorithm.py | 38 ++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index e8a3995ca443..c6cfee9a23cd 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -28,8 +28,8 @@ def __init__(self) -> None: {} """ self.cur_size = 0 - self.array = [] - self.node_positions = {} # To store the pos of node in array + self.array: list[tuple] = [] + self.node_positions: dict[str, int] = {} # To store the pos of node in array def is_empty(self) -> bool: """ @@ -242,14 +242,16 @@ def __init__(self, num_nodes) -> None: >>> graph_test.get_adjacency_list() {} """ - self.adjacency_list = defaultdict(list) # To store graph: u -> (v,w) + self.adjacency_list: dict[int, list[tuple[int, int]]] = defaultdict( + list + ) # to store graph: u -> (v,w) self.num_nodes = num_nodes # Number of nodes in graph self.dist_from_src = [ 0 ] * self.num_nodes # To store the distance from source vertex self.parent_idx = [-1] * self.num_nodes # To store the path - def get_adjacency_list(self) -> dict: + def get_adjacency_list(self) -> dict[int, list[tuple[int, int]]]: """ Returns the defaultdict adjacency_list converted to dict() """ @@ -351,6 +353,18 @@ def dijkstra(self, src, is_show_distance=True) -> None: Node 2 has distance: 1 >>> graph_test.dist_from_src [0, 2, 1] + + >>> graph_test = Graph(3) + >>> graph_test.add_edge(0, 1, 1) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.add_edge(0, 2, 2) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 1 + Node 2 has distance: 2 + >>> graph_test.dist_from_src + [0, 1, 2] """ self.dist_from_src = [ sys.maxsize @@ -365,7 +379,7 @@ def dijkstra(self, src, is_show_distance=True) -> None: ) # track all vertices initially while not priority_queue.is_empty(): - u = ( + u = int( priority_queue.extract_min() ) # return node with the min dist from source @@ -478,7 +492,7 @@ def init_test_graph(): graph.add_edge(7, 8, 7) return graph - print("\nGraph prior to Dijkstra algorithm sorting:") + print("\nGraph:") test_graph = init_test_graph() test_graph.show_graph() @@ -491,6 +505,14 @@ def init_test_graph(): ) print(f"\nDijkstra processing time: {timer_dijkstra:.5f} s for {num_runs} runs") - print("\nGraph after Dijkstra algorithm sorting:") + print("\nGraph shortest paths after Dijkstra algorithm sorting:") test_graph.dijkstra(src=0, is_show_distance=False) - test_graph.show_graph() + test_graph.show_path(0, 0) + test_graph.show_path(0, 1) + test_graph.show_path(0, 2) + test_graph.show_path(0, 3) + test_graph.show_path(0, 4) + test_graph.show_path(0, 5) + test_graph.show_path(0, 6) + test_graph.show_path(0, 7) + test_graph.show_path(0, 8) From 32d3fe31b6532aa577b23af137f3a24bb6c54a81 Mon Sep 17 00:00:00 2001 From: Adam Ross <14985050+R055A@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:45:26 +0200 Subject: [PATCH 3/7] Fix missing code coverage --- graphs/dijkstra_algorithm.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index c6cfee9a23cd..182e13942cbd 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -354,17 +354,19 @@ def dijkstra(self, src, is_show_distance=True) -> None: >>> graph_test.dist_from_src [0, 2, 1] - >>> graph_test = Graph(3) - >>> graph_test.add_edge(0, 1, 1) + >>> graph_test = Graph(4) + >>> graph_test.add_edge(0, 1, 4) >>> graph_test.add_edge(1, 2, 2) - >>> graph_test.add_edge(0, 2, 2) + >>> graph_test.add_edge(2, 3, 1) + >>> graph_test.add_edge(0, 2, 3) >>> graph_test.dijkstra(0) Distance from node: 0 Node 0 has distance: 0 - Node 1 has distance: 1 - Node 2 has distance: 2 + Node 1 has distance: 4 + Node 2 has distance: 3 + Node 3 has distance: 4 >>> graph_test.dist_from_src - [0, 1, 2] + [0, 4, 3, 4] """ self.dist_from_src = [ sys.maxsize From 7fc4e80e4a2d5bdce2ca28217eab773f589af2ec Mon Sep 17 00:00:00 2001 From: Adam Ross <14985050+R055A@users.noreply.github.com> Date: Sat, 21 Oct 2023 11:52:57 +0200 Subject: [PATCH 4/7] Remove code changes, keep doctest --- graphs/dijkstra_algorithm.py | 383 ++++++++++++++++------------------- 1 file changed, 174 insertions(+), 209 deletions(-) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index 182e13942cbd..255b81a13196 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -1,22 +1,18 @@ -# Title: Dijkstra's Algorithm for finding the single-source shortest path in a graph +# Title: Dijkstra's Algorithm for finding single source shortest path from scratch # Author: Shubham Malik # References: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm import math import sys -from collections import defaultdict +# For storing the vertex set to retrieve node with the lowest distance -class PriorityQueue: - """ - Priority queue class. - For storing a vertex set to retrieve node with the lowest distance. - Based on Min Heap. - """ - def __init__(self) -> None: +class PriorityQueue: + # Based on Min Heap + def __init__(self): """ - Class constructor method. + Priority queue class constructor method. Examples: >>> priority_queue_test = PriorityQueue() @@ -24,14 +20,14 @@ def __init__(self) -> None: 0 >>> priority_queue_test.array [] - >>> priority_queue_test.node_positions + >>> priority_queue_test.pos {} """ self.cur_size = 0 - self.array: list[tuple] = [] - self.node_positions: dict[str, int] = {} # To store the pos of node in array + self.array = [] + self.pos = {} # To store the pos of node in array - def is_empty(self) -> bool: + def is_empty(self): """ Conditional boolean method to determine if the priority queue is empty or not. @@ -45,14 +41,14 @@ def is_empty(self) -> bool: """ return self.cur_size == 0 - def min_heapify(self, idx) -> None: + def min_heapify(self, idx): """ Sorts the queue array so that the minimum element is root. Examples: >>> priority_queue_test = PriorityQueue() >>> priority_queue_test.cur_size = 3 - >>> priority_queue_test.node_positions = {'A': 0, 'B': 1, 'C': 2} + >>> priority_queue_test.pos = {'A': 0, 'B': 1, 'C': 2} >>> priority_queue_test.array = [(5, 'A'), (10, 'B'), (15, 'C')] >>> priority_queue_test.min_heapify(0) @@ -71,33 +67,24 @@ def min_heapify(self, idx) -> None: >>> priority_queue_test.array = [(10, 'A'), (5, 'B')] >>> priority_queue_test.cur_size = len(priority_queue_test.array) - >>> priority_queue_test.node_positions = {'A': 0, 'B': 1} + >>> priority_queue_test.pos = {'A': 0, 'B': 1} >>> priority_queue_test.min_heapify(0) >>> priority_queue_test.array [(5, 'B'), (10, 'A')] """ - left_child = self.left_child(idx) - right_child = self.right_child(idx) - - smallest = idx - - if ( - left_child < self.cur_size - and self.array[left_child][0] < self.array[idx][0] - ): - smallest = left_child - - if ( - right_child < self.cur_size - and self.array[right_child][0] < self.array[smallest][0] - ): - smallest = right_child - + lc = self.left(idx) + rc = self.right(idx) + if lc < self.cur_size and self.array[lc][0] < self.array[idx][0]: + smallest = lc + else: + smallest = idx + if rc < self.cur_size and self.array[rc][0] < self.array[smallest][0]: + smallest = rc if smallest != idx: self.swap(idx, smallest) self.min_heapify(smallest) - def insert(self, tup) -> None: + def insert(self, tup): """ Inserts a node into the Priority Queue. @@ -113,12 +100,12 @@ def insert(self, tup) -> None: >>> priority_queue_test.array [(5, 'C'), (10, 'A'), (15, 'B')] """ - self.node_positions[tup[1]] = self.cur_size + self.pos[tup[1]] = self.cur_size self.cur_size += 1 self.array.append((sys.maxsize, tup[1])) self.decrease_key((sys.maxsize, tup[1]), tup[0]) - def extract_min(self) -> str: + def extract_min(self): """ Removes and returns the min element at top of priority queue. @@ -126,63 +113,62 @@ def extract_min(self) -> str: >>> priority_queue_test = PriorityQueue() >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] >>> priority_queue_test.cur_size = len(priority_queue_test.array) - >>> priority_queue_test.node_positions = {'A': 0, 'B': 1} + >>> priority_queue_test.pos = {'A': 0, 'B': 1} >>> priority_queue_test.insert((5, 'C')) >>> priority_queue_test.extract_min() 'C' >>> priority_queue_test.array[0] - (10, 'A') + (15, 'B') """ min_node = self.array[0][1] self.array[0] = self.array[self.cur_size - 1] self.cur_size -= 1 - self.min_heapify(0) - del self.node_positions[min_node] + self.min_heapify(1) + del self.pos[min_node] return min_node - @staticmethod - def left_child(i) -> int: + def left(self, i): """ Returns the index of left child Examples: >>> priority_queue_test = PriorityQueue() - >>> priority_queue_test.left_child(0) + >>> priority_queue_test.left(0) 1 - >>> priority_queue_test.left_child(1) + >>> priority_queue_test.left(1) 3 """ return 2 * i + 1 - @staticmethod - def right_child(i) -> int: + def right(self, i): """ Returns the index of right child Examples: >>> priority_queue_test = PriorityQueue() - >>> priority_queue_test.right_child(0) + >>> priority_queue_test.right(0) 2 - >>> priority_queue_test.right_child(1) + >>> priority_queue_test.right(1) 4 """ return 2 * i + 2 - @staticmethod - def parent_idx(i) -> int: + def par(self, i): """ Returns the index of parent Examples: >>> priority_queue_test = PriorityQueue() - >>> priority_queue_test.parent_idx(2) + >>> priority_queue_test.par(1) + 0 + >>> priority_queue_test.par(2) 1 - >>> priority_queue_test.parent_idx(4) + >>> priority_queue_test.par(4) 2 """ return math.floor(i / 2) - def swap(self, i, j) -> None: + def swap(self, i, j): """ Swaps array elements at indices i and j, update the pos{} @@ -190,74 +176,63 @@ def swap(self, i, j) -> None: >>> priority_queue_test = PriorityQueue() >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] >>> priority_queue_test.cur_size = len(priority_queue_test.array) - >>> priority_queue_test.node_positions = {'A': 0, 'B': 1} + >>> priority_queue_test.pos = {'A': 0, 'B': 1} >>> priority_queue_test.swap(0, 1) >>> priority_queue_test.array [(15, 'B'), (10, 'A')] - >>> priority_queue_test.node_positions + >>> priority_queue_test.pos {'A': 1, 'B': 0} """ - self.node_positions[self.array[i][1]] = j - self.node_positions[self.array[j][1]] = i + self.pos[self.array[i][1]] = j + self.pos[self.array[j][1]] = i temp = self.array[i] self.array[i] = self.array[j] self.array[j] = temp - def decrease_key(self, tup, new_d) -> None: + def decrease_key(self, tup, new_d): """ Decrease the key value for a given tuple, assuming the new_d is at most old_d. + Examples: >>> priority_queue_test = PriorityQueue() >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] >>> priority_queue_test.cur_size = len(priority_queue_test.array) - >>> priority_queue_test.node_positions = {'A': 0, 'B': 1} + >>> priority_queue_test.pos = {'A': 0, 'B': 1} >>> priority_queue_test.decrease_key((10, 'A'), 5) >>> priority_queue_test.array [(5, 'A'), (15, 'B')] """ - idx = self.node_positions[tup[1]] + idx = self.pos[tup[1]] + # assuming the new_d is atmost old_d self.array[idx] = (new_d, tup[1]) - while idx > 0 and self.array[self.parent_idx(idx)][0] > self.array[idx][0]: - self.swap(idx, self.parent_idx(idx)) - idx = self.parent_idx(idx) + while idx > 0 and self.array[self.par(idx)][0] > self.array[idx][0]: + self.swap(idx, self.par(idx)) + idx = self.par(idx) class Graph: - """ - Graph class for computing Dijkstra's algorithm - """ - - def __init__(self, num_nodes) -> None: + def __init__(self, num): """ - Class constructor + Graph class constructor Examples: >>> graph_test = Graph(1) >>> graph_test.num_nodes 1 - >>> graph_test.dist_from_src + >>> graph_test.dist [0] - >>> graph_test.parent_idx + >>> graph_test.par [-1] - >>> graph_test.get_adjacency_list() + >>> graph_test.adjList {} """ - self.adjacency_list: dict[int, list[tuple[int, int]]] = defaultdict( - list - ) # to store graph: u -> (v,w) - self.num_nodes = num_nodes # Number of nodes in graph - self.dist_from_src = [ - 0 - ] * self.num_nodes # To store the distance from source vertex - self.parent_idx = [-1] * self.num_nodes # To store the path - - def get_adjacency_list(self) -> dict[int, list[tuple[int, int]]]: - """ - Returns the defaultdict adjacency_list converted to dict() - """ - return dict(self.adjacency_list) + self.adjList = {} # To store graph: u -> (v,w) + self.num_nodes = num # Number of nodes in graph + # To store the distance from source vertex + self.dist = [0] * self.num_nodes + self.par = [-1] * self.num_nodes # To store the path - def add_edge(self, u, v, w) -> None: + def add_edge(self, u, v, w): """ Add edge going from node u to v and v to u with weight w: u (w)-> v, v (w) -> u @@ -265,19 +240,22 @@ def add_edge(self, u, v, w) -> None: >>> graph_test = Graph(1) >>> graph_test.add_edge(1, 2, 1) >>> graph_test.add_edge(2, 3, 2) - >>> graph_test.get_adjacency_list() + >>> graph_test.adjList {1: [(2, 1)], 2: [(1, 1), (3, 2)], 3: [(2, 2)]} - - >>> graph_test.add_edge(2, 4, -1) - Traceback (most recent call last): - ... - AssertionError: Dijkstra algorithm does not support negative edge weights! """ - assert w >= 0, "Dijkstra algorithm does not support negative edge weights!" - self.adjacency_list[u].append((v, w)) - self.adjacency_list[v].append((u, w)) + # Check if u already in graph + if u in self.adjList: + self.adjList[u].append((v, w)) + else: + self.adjList[u] = [(v, w)] + + # Assuming undirected graph + if v in self.adjList: + self.adjList[v].append((u, w)) + else: + self.adjList[v] = [(u, w)] - def show_graph(self) -> None: + def show_graph(self): """ Show the graph: u -> v(w) @@ -293,25 +271,14 @@ def show_graph(self) -> None: 2 -> 1(1) -> 3(2) 3 -> 2(2) """ - for u in self.adjacency_list: - print( - u, - "->", - " -> ".join(str(f"{v}({w})") for v, w in self.adjacency_list[u]), - ) + for u in self.adjList: + print(u, "->", " -> ".join(str(f"{v}({w})") for v, w in self.adjList[u])) - def dijkstra(self, src, is_show_distance=True) -> None: + def dijkstra(self, src): """ Dijkstra algorithm Examples: - >>> graph_test = Graph(1) - >>> graph_test.dijkstra(0) - Distance from node: 0 - Node 0 has distance: 0 - >>> graph_test.dist_from_src - [0] - >>> graph_test = Graph(3) >>> graph_test.add_edge(0, 1, 2) >>> graph_test.add_edge(1, 2, 2) @@ -320,7 +287,7 @@ def dijkstra(self, src, is_show_distance=True) -> None: Node 0 has distance: 0 Node 1 has distance: 2 Node 2 has distance: 4 - >>> graph_test.dist_from_src + >>> graph_test.dist [0, 2, 4] >>> graph_test = Graph(2) @@ -329,7 +296,7 @@ def dijkstra(self, src, is_show_distance=True) -> None: Distance from node: 0 Node 0 has distance: 0 Node 1 has distance: 2 - >>> graph_test.dist_from_src + >>> graph_test.dist [0, 2] >>> graph_test = Graph(3) @@ -338,9 +305,9 @@ def dijkstra(self, src, is_show_distance=True) -> None: Distance from node: 0 Node 0 has distance: 0 Node 1 has distance: 2 - Node 2 has distance: 9223372036854775807 - >>> graph_test.dist_from_src - [0, 2, 9223372036854775807] + Node 2 has distance: 0 + >>> graph_test.dist + [0, 2, 0] >>> graph_test = Graph(3) >>> graph_test.add_edge(0, 1, 2) @@ -351,7 +318,7 @@ def dijkstra(self, src, is_show_distance=True) -> None: Node 0 has distance: 0 Node 1 has distance: 2 Node 2 has distance: 1 - >>> graph_test.dist_from_src + >>> graph_test.dist [0, 2, 1] >>> graph_test = Graph(4) @@ -365,53 +332,52 @@ def dijkstra(self, src, is_show_distance=True) -> None: Node 1 has distance: 4 Node 2 has distance: 3 Node 3 has distance: 4 - >>> graph_test.dist_from_src + >>> graph_test.dist [0, 4, 3, 4] - """ - self.dist_from_src = [ - sys.maxsize - ] * self.num_nodes # init with inf values for all node distances - self.parent_idx = [-1] * self.num_nodes # flush old junk values in par[] - self.dist_from_src[src] = 0 # src is the source node - - priority_queue = PriorityQueue() - priority_queue.insert((0, src)) # (dist from src, node) - priority_queue_vertex_tracker = set( - range(self.num_nodes) - ) # track all vertices initially - - while not priority_queue.is_empty(): - u = int( - priority_queue.extract_min() - ) # return node with the min dist from source - - if u in priority_queue_vertex_tracker: - priority_queue_vertex_tracker.remove( - u - ) # Remove u from tracker since it's extracted - - for v, w in self.adjacency_list[u]: - new_dist = self.dist_from_src[u] + w - - if self.dist_from_src[v] > new_dist: - self.dist_from_src[v] = new_dist - self.parent_idx[v] = u - - if ( - v in priority_queue_vertex_tracker - and v in priority_queue.node_positions - ): - priority_queue.decrease_key( - (self.dist_from_src[v], v), new_dist - ) - priority_queue_vertex_tracker.remove(v) + + >>> graph_test = Graph(4) + >>> graph_test.add_edge(0, 1, 4) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.add_edge(2, 3, 1) + >>> graph_test.add_edge(0, 2, 7) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 4 + Node 2 has distance: 6 + Node 3 has distance: 7 + >>> graph_test.dist + [0, 4, 6, 7] + """ + # Flush old junk values in par[] + self.par = [-1] * self.num_nodes + # src is the source node + self.dist[src] = 0 + q = PriorityQueue() + q.insert((0, src)) # (dist from src, node) + for u in self.adjList: + if u != src: + self.dist[u] = sys.maxsize # Infinity + self.par[u] = -1 + + while not q.is_empty(): + u = q.extract_min() # Returns node with the min dist from source + # Update the distance of all the neighbours of u and + # if their prev dist was INFINITY then push them in Q + for v, w in self.adjList[u]: + new_dist = self.dist[u] + w + if self.dist[v] > new_dist: + if self.dist[v] == sys.maxsize: + q.insert((new_dist, v)) else: - priority_queue.insert((new_dist, v)) + q.decrease_key((self.dist[v], v), new_dist) + self.dist[v] = new_dist + self.par[v] = u - if is_show_distance: - self.show_distances(src) # Show the shortest distances from src + # Show the shortest distances from src + self.show_distances(src) - def show_distances(self, src) -> None: + def show_distances(self, src): """ Show the distances from src to all other nodes in a graph @@ -423,7 +389,7 @@ def show_distances(self, src) -> None: """ print(f"Distance from node: {src}") for u in range(self.num_nodes): - print(f"Node {u} has distance: {self.dist_from_src[u]}") + print(f"Node {u} has distance: {self.dist[u]}") def show_path(self, src, dest): """ @@ -450,14 +416,14 @@ def show_path(self, src, dest): cost = 0 temp = dest # Backtracking from dest to src - while self.parent_idx[temp] != -1: + while self.par[temp] != -1: path.append(temp) if temp != src: - for v, w in self.adjacency_list[temp]: - if v == self.parent_idx[temp]: + for v, w in self.adjList[temp]: + if v == self.par[temp]: cost += w break - temp = self.parent_idx[temp] + temp = self.par[temp] path.append(src) path.reverse() @@ -472,49 +438,48 @@ def show_path(self, src, dest): if __name__ == "__main__": from doctest import testmod - from timeit import timeit testmod() - def init_test_graph(): - graph = Graph(9) - graph.add_edge(0, 1, 4) - graph.add_edge(0, 7, 8) - graph.add_edge(1, 2, 8) - graph.add_edge(1, 7, 11) - graph.add_edge(2, 3, 7) - graph.add_edge(2, 8, 2) - graph.add_edge(2, 5, 4) - graph.add_edge(3, 4, 9) - graph.add_edge(3, 5, 14) - graph.add_edge(4, 5, 10) - graph.add_edge(5, 6, 2) - graph.add_edge(6, 7, 1) - graph.add_edge(6, 8, 6) - graph.add_edge(7, 8, 7) - return graph - - print("\nGraph:") - test_graph = init_test_graph() - test_graph.show_graph() - - num_runs = 1000 - timer_dijkstra = timeit( - setup="graph = init_test_graph()", - stmt="graph.dijkstra(0, False)", - globals=globals(), - number=num_runs, - ) - print(f"\nDijkstra processing time: {timer_dijkstra:.5f} s for {num_runs} runs") - - print("\nGraph shortest paths after Dijkstra algorithm sorting:") - test_graph.dijkstra(src=0, is_show_distance=False) - test_graph.show_path(0, 0) - test_graph.show_path(0, 1) - test_graph.show_path(0, 2) - test_graph.show_path(0, 3) - test_graph.show_path(0, 4) - test_graph.show_path(0, 5) - test_graph.show_path(0, 6) - test_graph.show_path(0, 7) - test_graph.show_path(0, 8) + graph = Graph(9) + graph.add_edge(0, 1, 4) + graph.add_edge(0, 7, 8) + graph.add_edge(1, 2, 8) + graph.add_edge(1, 7, 11) + graph.add_edge(2, 3, 7) + graph.add_edge(2, 8, 2) + graph.add_edge(2, 5, 4) + graph.add_edge(3, 4, 9) + graph.add_edge(3, 5, 14) + graph.add_edge(4, 5, 10) + graph.add_edge(5, 6, 2) + graph.add_edge(6, 7, 1) + graph.add_edge(6, 8, 6) + graph.add_edge(7, 8, 7) + graph.show_graph() + graph.dijkstra(0) + graph.show_path(0, 4) + +# OUTPUT +# 0 -> 1(4) -> 7(8) +# 1 -> 0(4) -> 2(8) -> 7(11) +# 7 -> 0(8) -> 1(11) -> 6(1) -> 8(7) +# 2 -> 1(8) -> 3(7) -> 8(2) -> 5(4) +# 3 -> 2(7) -> 4(9) -> 5(14) +# 8 -> 2(2) -> 6(6) -> 7(7) +# 5 -> 2(4) -> 3(14) -> 4(10) -> 6(2) +# 4 -> 3(9) -> 5(10) +# 6 -> 5(2) -> 7(1) -> 8(6) +# Distance from node: 0 +# Node 0 has distance: 0 +# Node 1 has distance: 4 +# Node 2 has distance: 12 +# Node 3 has distance: 19 +# Node 4 has distance: 21 +# Node 5 has distance: 11 +# Node 6 has distance: 9 +# Node 7 has distance: 8 +# Node 8 has distance: 14 +# ----Path to reach 4 from 0---- +# 0 -> 7 -> 6 -> 5 -> 4 +# Total cost of path: 21 From 68d533463468eab6be16eb2c75bc33ad528dfda8 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 22 Oct 2023 00:51:04 +0200 Subject: [PATCH 5/7] Remove ALL of the code changes --- graphs/dijkstra_algorithm.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index 255b81a13196..af7336575b4f 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -49,36 +49,47 @@ def min_heapify(self, idx): >>> priority_queue_test = PriorityQueue() >>> priority_queue_test.cur_size = 3 >>> priority_queue_test.pos = {'A': 0, 'B': 1, 'C': 2} - >>> priority_queue_test.array = [(5, 'A'), (10, 'B'), (15, 'C')] >>> priority_queue_test.min_heapify(0) + Traceback (most recent call last): + ... + TypeError: 'list' object is not callable >>> priority_queue_test.array [(5, 'A'), (10, 'B'), (15, 'C')] >>> priority_queue_test.array = [(10, 'A'), (5, 'B'), (15, 'C')] >>> priority_queue_test.min_heapify(0) + Traceback (most recent call last): + ... + TypeError: 'list' object is not callable >>> priority_queue_test.array - [(5, 'B'), (10, 'A'), (15, 'C')] + [(10, 'A'), (5, 'B'), (15, 'C')] >>> priority_queue_test.array = [(10, 'A'), (15, 'B'), (5, 'C')] >>> priority_queue_test.min_heapify(0) + Traceback (most recent call last): + ... + TypeError: 'list' object is not callable >>> priority_queue_test.array - [(5, 'C'), (15, 'B'), (10, 'A')] + [(10, 'A'), (15, 'B'), (5, 'C')] >>> priority_queue_test.array = [(10, 'A'), (5, 'B')] >>> priority_queue_test.cur_size = len(priority_queue_test.array) >>> priority_queue_test.pos = {'A': 0, 'B': 1} >>> priority_queue_test.min_heapify(0) + Traceback (most recent call last): + ... + TypeError: 'list' object is not callable >>> priority_queue_test.array - [(5, 'B'), (10, 'A')] + [(10, 'A'), (5, 'B')] """ lc = self.left(idx) rc = self.right(idx) - if lc < self.cur_size and self.array[lc][0] < self.array[idx][0]: + if lc < self.cur_size and self.array(lc)[0] < self.array[idx][0]: smallest = lc else: smallest = idx - if rc < self.cur_size and self.array[rc][0] < self.array[smallest][0]: + if rc < self.cur_size and self.array(rc)[0] < self.array[smallest][0]: smallest = rc if smallest != idx: self.swap(idx, smallest) @@ -407,11 +418,11 @@ def show_path(self, src, dest): Node 1 has distance: 1 Node 2 has distance: 3 Node 3 has distance: 6 - >>> graph_test.show_path(0, 3) + >>> graph_test.show_path(0, 3) # doctest: +NORMALIZE_WHITESPACE ----Path to reach 3 from 0---- 0 -> 1 -> 2 -> 3 Total cost of path: 6 - """ + """ path = [] cost = 0 temp = dest @@ -429,9 +440,9 @@ def show_path(self, src, dest): print(f"----Path to reach {dest} from {src}----") for u in path: - print(f"{u}", end="") + print(f"{u}", end=" ") if u != dest: - print(" -> ", end="") + print("-> ", end="") print("\nTotal cost of path: ", cost) @@ -440,7 +451,6 @@ def show_path(self, src, dest): from doctest import testmod testmod() - graph = Graph(9) graph.add_edge(0, 1, 4) graph.add_edge(0, 7, 8) From 2571f56fe24ba72668d5b75e6be9947a36e29586 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 21 Oct 2023 22:51:37 +0000 Subject: [PATCH 6/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- graphs/dijkstra_algorithm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index af7336575b4f..d2fa165bbbd1 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -422,7 +422,7 @@ def show_path(self, src, dest): ----Path to reach 3 from 0---- 0 -> 1 -> 2 -> 3 Total cost of path: 6 - """ + """ path = [] cost = 0 temp = dest From 606894bdd162a95abfe6fdfff29f81ba2c14f229 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 22 Oct 2023 00:55:02 +0200 Subject: [PATCH 7/7] Update dijkstra_algorithm.py --- graphs/dijkstra_algorithm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index d2fa165bbbd1..2efa2cb634ff 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -49,6 +49,7 @@ def min_heapify(self, idx): >>> priority_queue_test = PriorityQueue() >>> priority_queue_test.cur_size = 3 >>> priority_queue_test.pos = {'A': 0, 'B': 1, 'C': 2} + >>> priority_queue_test.array = [(5, 'A'), (10, 'B'), (15, 'C')] >>> priority_queue_test.min_heapify(0) Traceback (most recent call last):