def get_shortest_path(graph: Graph, start: Vertex, end: Vertex): """ find shortest path between two vertices of graph * graph - instance of class of Graph type * start - start point (id or instance of Vertex class) * end - end point (id or instance of Vertex class) returns shortest path between ${start} and ${end} """ if not isinstance(graph, Graph): return None start_vertex = graph.get_vertex(start) end_vertex = graph.get_vertex(end) bfs = bfs_undirected_cyclic(graph, start_vertex) parent = bfs[1] if parent is None: return [] result = [end_vertex] current = end_vertex if current not in parent: return [] while parent[current] is not None: current = parent[current] result.append(current) return result
def bfs_undirected_cyclic(graph: Graph, start: Vertex): """ implementation of Breath-First-search algorithm for undirected cyclic graph - visit all nodes reachable from ${start} - (V+E) time (V - count of vertexes; E count of edges) - avoid duplicates graph - instance of class of Graph type start - id or instance of Vertex class *returns* all nodes reach from given ${start} and their levels """ if not isinstance(graph, Graph): return None, None start_vertex = graph.get_vertex(start) if not start_vertex: return None, None level = {start_vertex: 0} parent = {start_vertex: None} i = 1 frontier = [start_vertex] while frontier: _next = [] for parent_vertex in frontier: for child_id in parent_vertex.adj: child_vertex = parent_vertex.adj[child_id] if child_vertex not in level and child_vertex: level[child_vertex] = i parent[child_vertex] = parent_vertex _next.append(child_vertex) frontier = _next i += 1 return level, parent
def dfs_undirected_cyclic_b(graph: Graph, start: Vertex): """implementation of Depth-First-search algorithm for undirected cyclic graph. No vertex can be on the stack in more than one place. The size of S is thus not more than n. The time complexity is O(n + m). - (V+E) time (V - count of vertexes; E count of edges) graph - instance of class of Graph type start - id or instance of Vertex class *returns* all nodes reach from given ${start} and their levels """ if not isinstance(graph, Graph): return None, None start_vertex = graph.get_vertex(start) if not start_vertex: return None, None level = {start_vertex: 0} parent = {start_vertex: None} stack = [start_vertex] while stack: current_vertex = stack.pop() for child_id in current_vertex.adj: child_vertex = current_vertex.adj[child_id] if child_vertex not in level and child_vertex: level[child_vertex] = level[current_vertex] + 1 parent[child_vertex] = current_vertex stack.append(current_vertex) stack.append(child_vertex) break return level, parent
def dfs_undirected_cyclic(graph: Graph, start: Vertex): """implementation of Depth-First-search algorithm for undirected cyclic graph. Def can have multiple copies on the stack at the same time. However, the total number of iterations of the innermost loop of Def cannot exceed the number of edges of G, and thus the size of S cannot exceed m. The running time is O(n + m). - (V+E) time (V - count of vertexes; E count of edges) graph - instance of class of Graph type start - id or instance of Vertex class *returns* all nodes reach from given ${start} and their levels """ if not isinstance(graph, Graph): return None, None start_vertex = graph.get_vertex(start) if not start_vertex: return None, None level = {start_vertex: 0} parent = {start_vertex: None} stack = [start_vertex] while stack: current_vertex = stack.pop() for child_id in current_vertex.adj: child_vertex = current_vertex.adj[child_id] if child_vertex not in level and child_vertex: level[child_vertex] = level[current_vertex] + 1 parent[child_vertex] = current_vertex stack.append(child_vertex) return level, parent
def bellman_ford(graph: Graph, start, end): if not isinstance(graph, Graph): return None start_vertex = graph.get_vertex(start) if not start_vertex: return None end_vertex = graph.get_vertex(end) if not end_vertex: return None visited = {start_vertex.id: (0, None)} for _ in range(0, len(graph.vertexes)): for vertex in graph.vertexes.values(): # current vertex weigh weight = None if vertex.id in visited: weight = visited[vertex.id][0] # scanning edges for edge_id in vertex.adj.keys(): adj_weight = vertex.weights[edge_id] if edge_id in visited: current_weight = visited[edge_id][0] if weight and current_weight > adj_weight + weight: visited[edge_id] = (adj_weight + weight, vertex.id) elif weight is not None: visited[edge_id] = (adj_weight + weight, vertex.id) # check for negative cycle for vertex in graph.vertexes.values(): weight = visited[vertex.id][0] for edge_id in vertex.adj.keys(): adj_weight = vertex.weights[edge_id] current_weight = visited[edge_id][0] if current_weight > adj_weight + weight: # negative cycle return None # restore path if end_vertex.id not in visited: return None result = [end_vertex.id] vertex = end_vertex.id weight = visited[end_vertex.id][0] while vertex != start_vertex.id: vertex = visited[vertex][1] result.append(vertex) result.reverse() return weight, result
def single_path(graph: Graph, start, end): """implementation of dijkstra algorithm withOUT priority queue O(V**2) where V count vertexes(nodes) """ if not isinstance(graph, Graph): return None start_vertex = graph.get_vertex(start) if not start_vertex: return None end_vertex = graph.get_vertex(end) if not end_vertex: return None visited = {start_vertex: (0, None)} pq = [(0, start_vertex)] while pq: pq_vertex = heappop(pq) vertex = pq_vertex[1] weight = visited[vertex][0] for child_id, child_vertex in vertex.adj.items(): adj_weight = vertex.weights[child_id] if child_vertex in visited: current_weight = visited[child_vertex][0] if current_weight > adj_weight + weight: visited[child_vertex] = (adj_weight + weight, vertex) else: heappush(pq, (adj_weight + weight, child_vertex)) visited[child_vertex] = (adj_weight + weight, vertex) if end_vertex not in visited: return None result = [end_vertex.id] vertex = end_vertex weight = visited[end_vertex][0] while vertex != start_vertex: vertex = visited[vertex][1] result.append(vertex.id) result.reverse() return weight, result
def dfs_detect_cycle(graph: Graph, start: Vertex) -> bool: if not isinstance(graph, Graph): return False start_vertex = graph.get_vertex(start) if not start_vertex: return False level = {start_vertex: 0} stack = [start_vertex] while stack: current_vertex = stack.pop() for child_id in current_vertex.adj: child_vertex = current_vertex.adj[child_id] if child_vertex not in level and child_vertex: level[child_vertex] = level[current_vertex] + 1 stack.append(child_vertex) else: return True return False