diff --git a/README.md b/README.md index 477e006..4d588d7 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,8 @@ but it also contains other data structures, algorithms and problems. - [Fibonacci - recursive worst solution](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/fibonacci-recursive-worst-solution.py) - [Fibonacci - memoization](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/fibonacci-memoization.py) - [Fibonacci - recursive solution](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/fibonacci-recursive.py) -- [Fibonacci sum](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/fibonacci-sum-recursive.py) +- [Fibonacci sum recursive](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/fibonacci-sum-recursive.py) +- [Fibonacci sum iterative](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/fibonacci-sum-iterative.py) - [Maze - path finder](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/maze.py) - [Palindrome](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/palindrome.py) - [Reverse linked list - recursive solution](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/blob/master/recursion/reverse-linked-list.py) @@ -111,6 +112,7 @@ but it also contains other data structures, algorithms and problems. - [Dijkstra algorithm - Shortest path](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/tree/master/graphs/dijkstra) - [Priority Queue implementation](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/tree/master/graphs/dijkstra/priority-queue-impl-adjacency-map) - [Matrix implementation](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/tree/master/graphs/dijkstra/matrix-impl) + - [Adjacency list implementation](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/tree/master/graphs/dijkstra/adjacency-list-impl) - [Bellman-Ford algorithm](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/tree/master/graphs/bellman-ford) - [Bellman-Ford algorithm - negative weight cycle](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/tree/master/graphs/bellman-ford-negative-weight-cycle) - [Minimum Spanning Tree](https://github.com/ivanmmarkovic/Problem-Solving-with-Algorithms-and-Data-Structures-using-Python/tree/master/graphs/minimum-spanning-tree) diff --git a/graphs/bellman-ford/graph.py b/graphs/bellman-ford/graph.py index 5074fd2..2381d97 100755 --- a/graphs/bellman-ford/graph.py +++ b/graphs/bellman-ford/graph.py @@ -4,44 +4,75 @@ # to all other vertices in weighted graph. # Runtime O(V*E) +from typing import Set, Dict, List, Tuple + + class Graph: - def __init__(self): - self.vertices: list = [] - self.edges: list = [] - self.distance: dict = {} - self.prev: dict = {} - - def add_vertex(self, label: str): - self.vertices.append(label) - self.distance[label] = None + + + def __init__(self) -> None: + self.vertices:Set[str] = set() + self.edges:List[Tuple[str, str, int]] = list() + self.prev:Dict[str, str] = dict() + self.distances:Dict[str, int] = dict() + + + def add_vertex(self, label:str) -> None: + self.vertices.add(label) self.prev[label] = None + self.distances[label] = None - def add_edge(self, label1: str, label2: str, weight: int): - self.edges.append([label1, label2, weight]) - def bellman_ford(self, source: str): - self.distance[source] = 0 + def add_edge(self, v1:str, v2:str, distance:int) -> None: + self.edges.append((v1, v2, distance)) - for _ in range(len(self.vertices)): - for edge in self.edges: - label1: str = edge[0] - label2: str = edge[1] - weight: int = edge[2] + def bellman_ford(self, label:str) -> None: + self.distances[label] = 0 - if self.distance[label1] is None: - continue - if self.distance[label2] is None: - self.distance[label2] = self.distance[label1] + weight - self.prev[label2] = label1 - continue - if self.distance[label1] + weight < self.distance[label2]: - self.distance[label2] = self.distance[label1] + weight - self.prev[label2] = label1 + for _ in range(len(self.vertices) - 1): + + for v1, v2, distance in self.edges: + if self.distances[v1] is None: continue + if self.distances[v2] is None or self.distances[v2] > self.distances[v1] + distance: + self.distances[v2] = self.distances[v1] + distance + self.prev[v2] = v1 + + # Check for negative-weight cycles + for v1, v2, distance in self.edges: + if self.distances[v1] is not None and self.distances[v2] > self.distances[v1] + distance: + raise ValueError("Graph contains a negative-weight cycle") - def print_distances(self, source: str): + self._print_paths(label) + + + def _print_paths(self, label:str) -> None: for v in self.vertices: - if v != source: - distance: int = self.distance[v] - print(f'Distance from {source} to {v} is {distance}') \ No newline at end of file + if v == label: + continue + if self.distances[v] is not None: + print(f'Path from {label} to {v} is {self._return_path(v)} and distance is {self.distances[v]}') + else: + print(f'No path from {label} to {v}') + + + def _return_path(self, label:str) -> str: + if self.prev[label] is None: + return label + return self._return_path(self.prev[label]) + ' -> ' + label + + + +g = Graph() +for v in ['A', 'B', 'C', 'D']: + g.add_vertex(v) + +g.add_edge('A', 'B', 1) +g.add_edge('B', 'C', 3) +g.add_edge('A', 'C', 10) +g.add_edge('C', 'D', 2) +g.add_edge('D', 'B', 4) + +g.bellman_ford('A') + diff --git a/graphs/dijkstra/adjacency-list-impl/__pycache__/vertex.cpython-312.pyc b/graphs/dijkstra/adjacency-list-impl/__pycache__/vertex.cpython-312.pyc new file mode 100644 index 0000000..9a186d6 Binary files /dev/null and b/graphs/dijkstra/adjacency-list-impl/__pycache__/vertex.cpython-312.pyc differ diff --git a/graphs/dijkstra/adjacency-list-impl/main.py b/graphs/dijkstra/adjacency-list-impl/main.py new file mode 100644 index 0000000..2de3e50 --- /dev/null +++ b/graphs/dijkstra/adjacency-list-impl/main.py @@ -0,0 +1,87 @@ + +from typing import List, Dict, Set + +from vertex import Vertex + + +class Graph: + + def __init__(self, capacity :int =10): + self.capacity :int = capacity + self.vertices :Dict[str, Vertex] = dict() + self.prev :Dict[str, str] = dict() + self.adjacency_list :Dict[str, List[Vertex]] = dict() + self.visited :Set[str] = set() + + + + def add_vertex(self, label :str, weight :int = float('inf')) -> None: + v :Vertex = Vertex(label, weight) + self.vertices[v.label] = v + self.adjacency_list[label] = list() + self.prev[label] = None + + + def add_edge(self, label1 :str, label2 :str, weight :int) -> None: + v :Vertex = Vertex(label2, weight) + self.adjacency_list[label1].append(v) + + + def dijkstra(self, label :str) -> None: + v :Vertex = self.vertices[label] + v.weight = 0 + + while v is not None: + for n in self.adjacency_list[v.label]: + o :Vertex = self.vertices[n.label] + if v.weight + n.weight < o.weight: + o.weight = v.weight + n.weight + self.prev[o.label] = v.label + self.visited.add(v.label) + v = self._find_cheapest_vertex() + + + def show_path(self, label :str) -> str: + if self.prev[label] is None: + return label + return self.show_path(self.prev[label]) + '->' + label + + + def _find_cheapest_vertex(self) -> Vertex: + vertex :Vertex = None + for v in self.vertices.values(): + if v.label not in self.visited: + if vertex is None: + vertex = v + elif vertex.weight > v.weight: + vertex = v + + return vertex + + + +graph: Graph = Graph() + +graph.add_vertex("START") +graph.add_vertex("A") +graph.add_vertex("C") +graph.add_vertex("B") +graph.add_vertex("D") +graph.add_vertex("END") + +graph.add_edge("START", "A", 0) +graph.add_edge("START", "C", 2) +graph.add_edge("A", "B", 18) +graph.add_edge("A", "D", 15) +graph.add_edge("C", "B", 3) +graph.add_edge("C", "D", 10) +graph.add_edge("B", "END", 150) +graph.add_edge("D", "END", 15) +graph.dijkstra("START") + +print(graph.show_path("END")) + + + + + diff --git a/graphs/dijkstra/adjacency-list-impl/vertex.py b/graphs/dijkstra/adjacency-list-impl/vertex.py new file mode 100644 index 0000000..e87185a --- /dev/null +++ b/graphs/dijkstra/adjacency-list-impl/vertex.py @@ -0,0 +1,7 @@ + + +class Vertex: + + def __init__(self, label :str, weight :int = float('inf')): + self.label :str = label + self.weight :int = weight \ No newline at end of file diff --git a/graphs/is-graph-bipartite/graph.py b/graphs/is-graph-bipartite/graph.py index ba42c63..37bf939 100644 --- a/graphs/is-graph-bipartite/graph.py +++ b/graphs/is-graph-bipartite/graph.py @@ -16,7 +16,7 @@ def add_edge(self, label1: str = None, label2: str = None): self.adjacency_list[label1].append(label2) self.adjacency_list[label2].append(label1) - def is_bipartite(self) -> bool: + def bipartite_check(self) -> bool: for vertex in self.vertices: if self.color[vertex] is not None: continue diff --git a/graphs/kosarajus-algorithm/graph.py b/graphs/kosarajus-algorithm/graph.py index f57f10b..2a05e95 100755 --- a/graphs/kosarajus-algorithm/graph.py +++ b/graphs/kosarajus-algorithm/graph.py @@ -6,63 +6,66 @@ class Graph: + def __init__(self) -> None: - self.vertices: List[str] = [] - self.colors: Dict[str, str] = {} - self.adjacency_list: Dict[str, Set[str]] = {} - self.adjacency_list_reversed: Dict[str, Set[str]] = {} - self.stack: Stack = Stack() - self.components: List[List[str]] = [] - - def add_vertex(self, label:str): - self.vertices.append(label) - self.colors[label] = 'white' + self.vertices:Set[str] = set() + self.adjacency_list:Dict[str, Set[str]] = dict() + self.adjacency_list_reversed:Dict[str, Set[str]] = dict() + self.visited:Set[str] = set() + self.stack:Stack = Stack() + + + def add_vertex(self, label:str) -> None: + self.vertices.add(label) self.adjacency_list[label] = set() self.adjacency_list_reversed[label] = set() - def add_edge(self, label1:str, label2:str): + + def add_edge(self, label1:str, label2:str) -> None: + if label1 not in self.vertices or label2 not in self.vertices: + raise Exception('Vertices are not added') self.adjacency_list[label1].add(label2) self.adjacency_list_reversed[label2].add(label1) - def kosaraju(self): - for label in self.vertices: - if self.colors[label] == 'white': - self.dfs(label) - for color in self.colors: - self.colors[color] = 'white' + def kosaraju(self) -> List[List[str]]: + for v in self.vertices: + if v not in self.visited: + self._dfs(v) + + self.visited.clear() + + connected_components:List[List[str]] = list() while not self.stack.is_empty(): - current:str = self.stack.pop() + v:str = self.stack.pop() + if v not in self.visited: + connected:List[str] = list() + self._dfs_reversed(v, connected) - if self.colors[current] == 'white': - connected_components: List[str] = [] - self.dfs_reversed(current, connected_components) + if len(connected) > 0: + connected_components.append(connected) - self.components.append(connected_components) + return list(connected_components) - print(self.components) - def dfs(self, label:str): - self.colors[label] = 'gray' + def _dfs(self, label:str) -> None: + self.visited.add(label) for n in self.adjacency_list[label]: - if self.colors[n] == 'white': - self.dfs(n) + if n not in self.visited: + self._dfs(n) - self.colors[label] = 'black' self.stack.push(label) - def dfs_reversed(self, label: str, connected_components:List[str]): - self.colors[label] = 'gray' - - for n in self.adjacency_list_reversed[label]: - if self.colors[n] == 'white': - self.dfs_reversed(n, connected_components) - connected_components.append(label) - self.colors[label] = 'black' + def _dfs_reversed(self, v:str, connected:List[str]) -> None: + connected.append(v) + self.visited.add(v) + for n in self.adjacency_list_reversed[v]: + if n not in self.visited: + self._dfs_reversed(n, connected) diff --git a/graphs/kosarajus-algorithm/main.py b/graphs/kosarajus-algorithm/main.py index dd91e6b..c3b4ca9 100755 --- a/graphs/kosarajus-algorithm/main.py +++ b/graphs/kosarajus-algorithm/main.py @@ -15,4 +15,4 @@ g.add_edge('7', '6') g.add_edge('8', '7') -g.kosaraju() +print(g.kosaraju()) diff --git a/graphs/minimum-spanning-tree/kruskals-algorithm/graph.py b/graphs/minimum-spanning-tree/kruskals-algorithm/graph.py index 8f35405..028107d 100644 --- a/graphs/minimum-spanning-tree/kruskals-algorithm/graph.py +++ b/graphs/minimum-spanning-tree/kruskals-algorithm/graph.py @@ -1,57 +1,53 @@ -from vertex import Vertex - -def find_root(vertex: Vertex = None) -> Vertex: - while vertex.root != vertex: - vertex = vertex.root - return vertex - -def sort_edges_by_weight_increasingly(edges: list) -> list: - # edges [[label1, label2, weight], [label1, label2, weight]] - if len(edges) < 2: - return edges - - pivot_index: int = len(edges) // 2 - pivot_value = edges[pivot_index][2] - left_list: list = [] - right_list: list = [] - for i in range(len(edges)): - if i != pivot_index: - if edges[i][2] < pivot_value: - left_list.append(edges[i]) - else: - right_list.append(edges[i]) - - return sort_edges_by_weight_increasingly(left_list) + [edges[pivot_index]] + sort_edges_by_weight_increasingly(right_list) - +from typing import List, Set, Dict, Tuple class Graph: - def __init__(self): - self.vertices: dict = {} - self.edges: list = [] - - def add_vertex(self, label: str = None): - self.vertices[label] = Vertex(label) - - def add_edge(self, vertex_1_label: str = None, vertex_2_label: str = None, weight: int = float("inf")): - if vertex_1_label not in self.vertices or vertex_2_label not in self.vertices: - raise Exception("Invalid label name") - self.edges.append([vertex_1_label, vertex_2_label, weight]) - self.edges.append([vertex_2_label, vertex_1_label, weight]) - - def kruskal(self): - # sort edges by weight increasingly - self.edges = sort_edges_by_weight_increasingly(self.edges) - minimum_spanning_tree: list = [] - for edge_list in self.edges: - vertex_one: Vertex = self.vertices[edge_list[0]] - vertex_two: Vertex = self.vertices[edge_list[1]] - - root_one: Vertex = find_root(vertex_one) - root_two: Vertex = find_root(vertex_two) - - if root_one != root_two: - root_one.root = root_two - minimum_spanning_tree.append([vertex_one.label, vertex_two.label]) - - print(minimum_spanning_tree) \ No newline at end of file + + + def __init__(self) -> None: + self.vertices:Set[str] = set() + self.roots:Dict[str, str] = dict() + self.sizes:Dict[str, int] = dict() + self.edges:List[Tuple[str, str, int]] = list() + self.mst:List[Tuple[str, str, int]] = list() + + + def add_vertex(self, label:str) -> None: + self.vertices.add(label) + self.roots[label] = label + self.sizes[label] = 1 + + + def add_edge(self, label1:str, label2:str, weight:int) -> None: + if label1 not in self.vertices or label2 not in self.vertices: + raise Exception("Vertices must be added before connecting them") + self.edges.append((label1, label2, weight)) + + + def kruskal(self) -> List[Tuple[str, str, int]]: + self.mst.clear() + self.edges.sort(key = lambda edge: edge[2]) + + for v1, v2, weight in self.edges: + + root1:str = self._find_root(v1) + root2:str = self._find_root(v2) + + if root1 != root2: + if self.sizes[root1] >= self.sizes[root2]: + self.roots[root2] = root1 + self.sizes[root1] += self.sizes[root2] + else: + self.roots[root1] = root2 + self.sizes[root2] += self.sizes[root1] + self.mst.append((v1, v2, weight)) + + return list(self.mst) + + def _find_root(self, label:str) -> str: + if self.roots[label] != label: + self.roots[label] = self._find_root(self.roots[label]) + return self.roots[label] + + + diff --git a/graphs/minimum-spanning-tree/kruskals-algorithm/main.py b/graphs/minimum-spanning-tree/kruskals-algorithm/main.py index 0caa660..e761e99 100644 --- a/graphs/minimum-spanning-tree/kruskals-algorithm/main.py +++ b/graphs/minimum-spanning-tree/kruskals-algorithm/main.py @@ -19,4 +19,4 @@ g.add_edge('c', 'f', 2) g.add_edge('f', 'e', 1) -g.kruskal() +print(g.kruskal()) diff --git a/graphs/minimum-spanning-tree/kruskals-algorithm/vertex.py b/graphs/minimum-spanning-tree/kruskals-algorithm/vertex.py deleted file mode 100644 index e18cd8d..0000000 --- a/graphs/minimum-spanning-tree/kruskals-algorithm/vertex.py +++ /dev/null @@ -1,4 +0,0 @@ -class Vertex: - def __init__(self, label: str = None): - self.label: str = label - self.root = self diff --git a/graphs/minimum-spanning-tree/prims-algorithm/graph.py b/graphs/minimum-spanning-tree/prims-algorithm/graph.py index a477449..ff718d7 100644 --- a/graphs/minimum-spanning-tree/prims-algorithm/graph.py +++ b/graphs/minimum-spanning-tree/prims-algorithm/graph.py @@ -20,7 +20,7 @@ def add_vertex(self, label:str, weight:int=float('inf')): def add_edge(self, label1:str, label2:str, weight:int): self.adjacency_map[label1].append(Vertex(label2, weight)) - self.adjacency_map[label1].append(Vertex(label1, weight)) + self.adjacency_map[label2].append(Vertex(label1, weight)) def prims(self, label:str): diff --git a/hash-table/chaining.py b/hash-table/chaining.py index d022cdb..c7f4d3a 100644 --- a/hash-table/chaining.py +++ b/hash-table/chaining.py @@ -90,8 +90,12 @@ def delete(self, key: int) -> None: found = True else: i += 1 - if found: - items.pop(i) + if not found: + return None + + items.pop(i) + if len(items) == 0: + self.table[index] = None return None diff --git a/queue/queue-array-impl.py b/queue/queue-array-impl.py index e4df970..8bb8b70 100644 --- a/queue/queue-array-impl.py +++ b/queue/queue-array-impl.py @@ -6,6 +6,8 @@ def enqueue(self, item): self._queue.insert(0, item) def dequeue(self): + if self.is_empty(): + raise Exception('Queue is empty') return self._queue.pop() def size(self) -> int: diff --git a/recursion/convert-number-iterative.py b/recursion/convert-number-iterative.py index 41bfa15..e43b723 100644 --- a/recursion/convert-number-iterative.py +++ b/recursion/convert-number-iterative.py @@ -13,4 +13,16 @@ def converter(num: int, base: int) -> str: return result -print(converter(1453, 16)) # 5AD \ No newline at end of file +''' +digits = "0123456789ABCDEF" +def converter(num: int, base: int) -> str: + + r:str = '' + while num > 0: + r = digits[num % base] + r + num = num // base + + return r +''' + +print(converter(1453, 16)) # 5AD