@@ -700,12 +700,14 @@ def shortest_paths(graph: Graph, algorithm: str,
700700 'bellman_ford' -> Bellman-Ford algorithm as given in [1].
701701
702702 'dijkstra' -> Dijkstra algorithm as given in [2].
703+
704+ 'A_star' -> A* algorithm as given in [3].
703705 source: str
704706 The name of the source the node.
705707 target: str
706708 The name of the target node.
707709 Optional, by default, all pair shortest paths
708- are returned.
710+ are returned. Required for A* algorithm.
709711 backend: pydatastructs.Backend
710712 The backend to be used.
711713 Optional, by default, the best available
@@ -736,12 +738,20 @@ def shortest_paths(graph: Graph, algorithm: str,
736738 ({'V1': 0, 'V2': 11, 'V3': 21}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
737739 >>> shortest_paths(G, 'dijkstra', 'V1')
738740 ({'V2': 11, 'V3': 21, 'V1': 0}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
739-
741+ >>> start = AdjacencyListGraphNode("0,0")
742+ >>> middle = AdjacencyListGraphNode("1,1")
743+ >>> goal = AdjacencyListGraphNode("2,2")
744+ >>> G2 = Graph(start, middle, goal)
745+ >>> G2.add_edge('0,0', '1,1', 2)
746+ >>> G2.add_edge('1,1', '2,2', 2)
747+ >>> shortest_paths(G2, 'A_star', '0,0', '2,2')
748+ (4, {'0,0': None, '1,1': '0,0', '2,2': '1,1'})
740749 References
741750 ==========
742751
743752 .. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
744753 .. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
754+ .. [3] https://en.wikipedia.org/wiki/A*_search_algorithm
745755 """
746756 raise_if_backend_is_not_python (
747757 shortest_paths , kwargs .get ('backend' , Backend .PYTHON ))
@@ -811,6 +821,58 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str):
811821
812822_dijkstra_adjacency_matrix = _dijkstra_adjacency_list
813823
824+ def _a_star_adjacency_list (graph : Graph , source : str , target : str ) -> tuple :
825+ """
826+ A* pathfinding algorithm implementation similar to Dijkstra's structure.
827+
828+ Parameters
829+ ==========
830+ graph: Graph
831+ The graph to search through
832+ source: str
833+ Starting node name
834+ target: str
835+ Target node name
836+
837+ Returns
838+ =======
839+ (distance, predecessors): tuple
840+ Distance to target and dictionary of predecessors
841+ """
842+ def heuristic (node : str , goal : str ) -> float :
843+ """Manhattan distance heuristic for A*"""
844+ x1 , y1 = map (int , node .split (',' ))
845+ x2 , y2 = map (int , goal .split (',' ))
846+ return abs (x1 - x2 ) + abs (y1 - y2 )
847+ visited = {v : False for v in graph .vertices }
848+ dist = {v : float ('inf' ) for v in graph .vertices }
849+ pred = {v : None for v in graph .vertices }
850+ dist [source ] = 0
851+ # Priority queue using f-score (g_score + heuristic)
852+ pq = PriorityQueue (implementation = 'binomial_heap' )
853+ pq .push (source , heuristic (source , target ))
854+ while not pq .is_empty :
855+ current = pq .pop ()
856+ if current == target :
857+ return dist [target ], pred
858+ if visited [current ]:
859+ continue
860+ visited [current ] = True
861+ for neighbor in graph .neighbors (current ):
862+ if visited [neighbor .name ]:
863+ continue
864+ edge = graph .get_edge (current , neighbor .name )
865+ if not edge :
866+ continue
867+ new_dist = dist [current ] + edge .value
868+ if new_dist < dist [neighbor .name ]:
869+ dist [neighbor .name ] = new_dist
870+ pred [neighbor .name ] = current
871+ f_score = new_dist + heuristic (neighbor .name , target )
872+ pq .push (neighbor .name , f_score )
873+ return float ('inf' ), pred
874+ _a_star_adjacency_matrix = _a_star_adjacency_list
875+
814876def all_pair_shortest_paths (graph : Graph , algorithm : str ,
815877 ** kwargs ) -> tuple :
816878 """
0 commit comments