@@ -736,7 +736,10 @@ def shortest_paths(graph: Graph, algorithm: str,
736736 ({'V1': 0, 'V2': 11, 'V3': 21}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
737737 >>> shortest_paths(G, 'dijkstra', 'V1')
738738 ({'V2': 11, 'V3': 21, 'V1': 0}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
739-
739+ >>> grid_graph = Graph(AdjacencyListGraphNode("0,0"), AdjacencyListGraphNode("1,1"))
740+ >>> grid_graph.add_edge('0,0', '1,1', 2)
741+ >>> shortest_paths(grid_graph, 'a_star_with_manhattan', '0,0', '1,1')
742+ (2, {'1,1': '0,0'})
740743 References
741744 ==========
742745
@@ -811,6 +814,75 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str):
811814
812815_dijkstra_adjacency_matrix = _dijkstra_adjacency_list
813816
817+ def _a_star_with_manhattan_adjacency_list (graph : Graph , start : str , target : str , ** kwargs ):
818+ """
819+ A* algorithm with Manhattan distance as the heuristic function for grid-based graphs.
820+ """
821+ def manhattan_distance (node1 : str , node2 : str ) -> float :
822+ try :
823+ x1 , y1 = map (int , node1 .split ("," ))
824+ x2 , y2 = map (int , node2 .split ("," ))
825+ return abs (x1 - x2 ) + abs (y1 - y2 )
826+ except (ValueError , TypeError ):
827+ raise ValueError (f"Invalid node format. Expected 'x,y', got { node1 } or { node2 } " )
828+
829+ # Validate inputs
830+ if start == target :
831+ return 0 , {start : None }
832+
833+ if start not in graph .vertices or target not in graph .vertices :
834+ raise ValueError (f"Start or target node not in graph. Start: { start } , Target: { target } " )
835+
836+ # Initialize data structures
837+ g_score = {v : float ('inf' ) for v in graph .vertices }
838+ f_score = {v : float ('inf' ) for v in graph .vertices }
839+ pred = {v : None for v in graph .vertices }
840+ visited = {v : False for v in graph .vertices }
841+
842+ # Initialize start node
843+ g_score [start ] = 0
844+ f_score [start ] = manhattan_distance (start , target )
845+
846+ # Priority queue for A* algorithm
847+ pq = PriorityQueue (implementation = 'binomial_heap' )
848+ pq .push (start , f_score [start ])
849+
850+ while not pq .is_empty :
851+ current = pq .pop ()
852+
853+ # Goal reached
854+ if current == target :
855+ return g_score [target ], pred
856+
857+ visited [current ] = True
858+
859+ # Explore neighbors
860+ for neighbor in graph .neighbors (current ):
861+ if visited [neighbor .name ]:
862+ continue
863+
864+ edge = graph .get_edge (current , neighbor .name )
865+ if not edge :
866+ continue
867+
868+ # Calculate tentative g_score
869+ tentative_g_score = g_score [current ] + edge .value
870+
871+ # Update if better path found
872+ if tentative_g_score < g_score [neighbor .name ]:
873+ pred [neighbor .name ] = current
874+ g_score [neighbor .name ] = tentative_g_score
875+ f_score [neighbor .name ] = (
876+ tentative_g_score +
877+ manhattan_distance (neighbor .name , target )
878+ )
879+ pq .push (neighbor .name , f_score [neighbor .name ])
880+
881+ # No path exists
882+ raise ValueError (f"No path exists between { start } and { target } " )
883+
884+ _a_star_with_manhattan_adjacency_matrix = _a_star_with_manhattan_adjacency_list
885+
814886def all_pair_shortest_paths (graph : Graph , algorithm : str ,
815887 ** kwargs ) -> tuple :
816888 """
0 commit comments