class TestFibonacciHeap(unittest.TestCase): def setUp(self): self.list = [random.randint(1, 20) for _ in range(10)] self.heap = FibonacciHeap(self.list) self.list2 = [random.randint(1, 20) for _ in range(10)] self.heap2 = FibonacciHeap(self.list2) def test_pop(self): start = 0 while True: try: value = self.heap.pop() assert value >= start assert value in self.list self.list.remove(value) start = value except IndexError: break assert len(self.list) == 0 def test_insert(self): heap = FibonacciHeap() heap.insert(4) heap.insert(7) heap.insert(1) heap.insert(2) heap.insert(10) assert heap.pop() == 1 heap.insert(3) assert heap.pop() == 2 assert heap.pop() == 3 assert heap.pop() == 4 assert heap.pop() == 7 assert heap.pop() == 10 def test_merge(self): self.heap.merge(self.heap2) assert len(self.heap) == 20 start = 0 combined_list = self.list + self.list2 while True: try: value = self.heap.pop() assert value >= start assert value in combined_list combined_list.remove(value) start = value except IndexError: break assert len(combined_list) == 0
def prim(graph: UndirectedGraph) -> UndirectedGraph: """ Prim's algorithm is a greedy algorithm that finds a minimum spanning tree for a weighted undirected graph. This means it finds a subset of the edges that forms a tree that includes every vertex O(E + V log V) """ current_vertex = graph.get_vertices()[0] new_graph = UndirectedGraph() heap = FibonacciHeap(map(lambda x: (x, current_vertex), graph.get_outgoing_edges(current_vertex))) vertices = {current_vertex} while not heap.is_empty(): edge, current_vertex = heap.pop() next_vertex = edge.a if edge.a != current_vertex else edge.b if next_vertex in vertices: continue # Add edge to the minimum spanning tree vertices.add(next_vertex) new_graph.add_edge(current_vertex, next_vertex, edge.weight) # Add all edges from this new point to the heap [heap.insert((edge, next_vertex)) for edge in graph.get_outgoing_edges(next_vertex)] return new_graph
def test_insert(self): heap = FibonacciHeap() heap.insert(4) heap.insert(7) heap.insert(1) heap.insert(2) heap.insert(10) assert heap.pop() == 1 heap.insert(3) assert heap.pop() == 2 assert heap.pop() == 3 assert heap.pop() == 4 assert heap.pop() == 7 assert heap.pop() == 10
def dijkstra(graph: Graph, start_node, end_node) -> (int, List): """ Dijkstras shortest path algorithm implementation Works for directed and undirected graphs with non negative weights Time complexity: O(EV + V^2 * logV) :return Returns a tuple of the distance of the shortest path and the nodes to reach the shortest path """ # Min heap pq = FibonacciHeap() pq.insert((0, start_node)) # Final distances and paths from the start node distances = {key: sys.maxsize for key in graph.get_vertices()} paths = {key: [] for key in graph.get_vertices()} # Finished nodes visited_nodes = set() # Set start node distance to 0 distances[start_node] = 0 while not pq.is_empty(): distance, vertex = pq.pop() if distance == sys.maxsize or end_node in visited_nodes: # Only unconnected vertices are left or we found the minimal distance break for outgoing_edge in graph.get_outgoing_edges(vertex): next_vertex = outgoing_edge.a if outgoing_edge.a != vertex else outgoing_edge.b if next_vertex in visited_nodes: continue if distance + outgoing_edge.weight < distances[next_vertex]: distances[next_vertex] = distance + outgoing_edge.weight paths[next_vertex] = paths[vertex] + [next_vertex] pq.insert((distance + outgoing_edge.weight, next_vertex)) visited_nodes.add(vertex) return distances[end_node], paths[end_node]
def kruskal(graph: UndirectedGraph) -> UndirectedGraph: """ Kruskal's algorithm is a minimum-spanning-tree algorithm which finds an edge of the least possible weight that connects any two trees in the forest. It is a greedy algorithm in graph theory as it finds a minimum spanning tree for a connected weighted undirected graph adding increasing cost arcs at each step. Time complexity: O(E log V) """ spanning_tree = UndirectedGraph() union_find = UnionFind(max(graph.get_vertices()) + 1) heap = FibonacciHeap(graph.get_all_edges()) while not heap.is_empty(): edge = heap.pop() # Only add the edge if it will not create a cycle if union_find.find(edge.a) != union_find.find(edge.b): spanning_tree.add_edge(edge.a, edge.b, edge.weight) union_find.union(edge.a, edge.b) return spanning_tree