def topological_sort_kahn(graph: Graph) -> list: """ Ex 22.4-5. If return list is shorter than graph vertex length, it means the topological sort cannot be performed. O(V + E) time. """ in_degrees = {} ret = [] for v_key in graph.vertex_keys(): in_degrees[v_key] = 0 for u_key in graph.vertex_keys(): for v_key, _ in graph.get_vertex(u_key).successors(): in_degrees[v_key] += 1 zero_in_degree_set = deque() for v_key in graph.vertex_keys(): if in_degrees[v_key] == 0: zero_in_degree_set.append(v_key) while zero_in_degree_set: u_key = zero_in_degree_set.popleft() ret.append(u_key) for v_key, _ in list(graph.get_vertex(u_key).successors()): graph.remove_edge(u_key, v_key) in_degrees[v_key] -= 1 if in_degrees[v_key] == 0: zero_in_degree_set.append(v_key) return ret
def dfs_internal(graph: Graph, src_key, pre_visit_func: Callable[[Any], bool], post_visit_func: Callable[[Any], bool], result: DFSResult, time: TimeCounter) -> bool: if not pre_visit_func(src_key): return False stack = [(src_key, graph.get_vertex(src_key).successors())] result.came_from[src_key] = None result.discover_times[src_key] = time.next() default_next_item = object() while stack: cur_key, iter = stack[-1] next_item = next(iter, default_next_item) if next_item == default_next_item: stack.pop(-1) result.finish_times[cur_key] = time.next() if not post_visit_func(cur_key): return False continue successor_key = next_item[0] if successor_key in result.discover_times: continue if not pre_visit_func(successor_key): return False result.came_from[successor_key] = cur_key result.discover_times[successor_key] = time.next() stack.append( (successor_key, graph.get_vertex(successor_key).successors())) return True
def _tree_diameter_recur_internal(tree: Graph, root_key, parents: set) -> Tuple[int, int]: diameter, depth = 0, 0 root = tree.get_vertex(root_key) parents.add(root_key) successor_len = 0 for v_key, _ in root.successors(): if v_key not in parents: successor_len += 1 if successor_len <= 0: # print('root_key=%d, diameter=%d, depth=%d (leaf)' % (root_key, diameter, depth)) return diameter, depth max_sub_diameter, max_sub_depth, second_sub_depth = 0, -1, -1 for v_key, _ in root.successors(): if v_key in parents: continue sub_diameter, sub_depth = _tree_diameter_recur_internal(tree, v_key, parents) max_sub_diameter = max(max_sub_diameter, sub_diameter) if max_sub_depth < 0: max_sub_depth = sub_depth elif sub_depth >= max_sub_depth: second_sub_depth = max_sub_depth max_sub_depth = sub_depth elif sub_depth >= second_sub_depth: second_sub_depth = sub_depth depth = max_sub_depth + 1 diameter = max(max_sub_depth + 1 if second_sub_depth < 0 else max_sub_depth + second_sub_depth + 2, max_sub_diameter) # print('root_key=%d, diameter=%d, depth=%d' % (root_key, diameter, depth)) return diameter, depth
def graph_to_adj_matrix(graph: Graph): vertex_count = graph.vertex_len adj_matrix = [([0] * vertex_count) for _ in range(vertex_count)] for u in graph.vertex_keys(): for v, _ in graph.get_vertex(u).successors(): adj_matrix[u][v] = 1 return adj_matrix
def get_transpose(graph: Graph): """Ex 22.1-3.""" tr = Graph() for v in graph.vertex_keys(): tr.add_vertex(Vertex(v)) for u in graph.vertex_keys(): for v, weight in graph.get_vertex(u).successors(): tr.add_edge(v, u, weight) return tr
def _euler(graph: Graph, path: list, src_index: int) -> Tuple[list, int]: src_key = path[src_index] src = graph.get_vertex(src_key) u = src sub_path = [src_key] while u.successor_len > 0: v = None for v_key, _ in u.successors(): v = graph.get_vertex(v_key) break graph.remove_edge(u.key, v.key) graph.remove_edge(v.key, u.key) sub_path.append(v.key) u = v i = 1 while i < len(sub_path): sub_path, delta_len = _euler(graph, sub_path, i) i += delta_len path = path[0:src_index] + sub_path + path[src_index + 1:] return path, len(sub_path)
def dijkstra(graph: Graph, src_key) -> dict: open_set = {src_key: 0} closed_set = set() came_from = {src_key: None} while open_set: u, u_cost = extract_min(open_set) closed_set.add(u) for v, w in graph.get_vertex(u).successors(): if v in closed_set: continue new_cost = u_cost + w if v not in open_set or open_set[v] > new_cost: came_from[v] = u open_set[v] = new_cost return came_from
def _bfs_internal(graph: Graph, src_key, visit_func: Callable[[Any], bool], open_set: deque, came_from: dict, depths: dict, visited: set): open_set.appendleft(src_key) visited.add(src_key) depths[src_key] = 0 while open_set: u = open_set.pop() if not visit_func(u): break for v, _ in graph.get_vertex(u).successors(): if v in visited: continue came_from[v] = u open_set.appendleft(v) visited.add(v) depths[v] = depths[u] + 1
def _tarjan_internal(graph: Graph, src_key, sccs: List[List], cache: TarjanCache, time: TimeCounter): cache.in_stack.add(src_key) cache.stack.append(src_key) cache.discover_times[src_key] = cache.low[src_key] = time.next() for v_key, _ in graph.get_vertex(src_key).successors(): if v_key not in cache.discover_times: _tarjan_internal(graph, v_key, sccs, cache, time) cache.low[src_key] = min(cache.low[src_key], cache.low[v_key]) elif v_key not in cache.finish_times: cache.low[src_key] = min(cache.low[src_key], cache.discover_times[v_key]) cache.finish_times[src_key] = time.next() if cache.low[src_key] == cache.discover_times[src_key]: scc = [] while True: v_key = cache.stack.pop() cache.in_stack.remove(v_key) scc.append(v_key) if v_key == src_key: break sccs.append(scc)
def count_simple_path(graph: Graph, src_key, dst_key) -> int: """Ex 22.4-2. O(V + E) time.""" sort_result = topological_sort_dfs(graph) src_index, dst_index = -1, -1 for i in range(len(sort_result)): if sort_result[i] == src_key: src_index = i if sort_result[i] == dst_key: dst_index = i assert src_index >= 0 assert dst_index >= 0 if src_index >= dst_index: return 0 counts = [0] * (dst_index - src_index + 1) checked_vertices = {} for i in range(dst_index - 1, src_index - 1, -1): checked_vertices[sort_result[i]] = i for v_key, _ in graph.get_vertex(sort_result[i]).successors(): if v_key == dst_key: counts[i] += 1 elif v_key in checked_vertices: counts[i] += counts[checked_vertices[v_key]] return counts[0]
def astar(graph: Graph, src_key, dst_key, heuristic_func: Callable[[Any], float] = None) -> dict: open_set = {src_key: 0} closed_set = set() came_from = {src_key: None} while open_set: u, u_cost = extract_min(open_set, heuristic_func) # Diff from Dijkstra closed_set.add(u) if u == dst_key: # Diff from dijkstra break for v, w in graph.get_vertex(u).successors(): if v in closed_set: continue new_cost = u_cost + graph.edge_weight(u, v) if v not in open_set or open_set[v] > new_cost: open_set[v] = new_cost came_from[v] = u return came_from