@@ -700,12 +700,14 @@ def shortest_paths(graph: Graph, algorithm: str,
700
700
'bellman_ford' -> Bellman-Ford algorithm as given in [1].
701
701
702
702
'dijkstra' -> Dijkstra algorithm as given in [2].
703
+
704
+ 'A_star' -> A* algorithm as given in [3].
703
705
source: str
704
706
The name of the source the node.
705
707
target: str
706
708
The name of the target node.
707
709
Optional, by default, all pair shortest paths
708
- are returned.
710
+ are returned. Required for A* algorithm.
709
711
backend: pydatastructs.Backend
710
712
The backend to be used.
711
713
Optional, by default, the best available
@@ -736,12 +738,20 @@ def shortest_paths(graph: Graph, algorithm: str,
736
738
({'V1': 0, 'V2': 11, 'V3': 21}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
737
739
>>> shortest_paths(G, 'dijkstra', 'V1')
738
740
({'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'})
740
749
References
741
750
==========
742
751
743
752
.. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
744
753
.. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
754
+ .. [3] https://en.wikipedia.org/wiki/A*_search_algorithm
745
755
"""
746
756
raise_if_backend_is_not_python (
747
757
shortest_paths , kwargs .get ('backend' , Backend .PYTHON ))
@@ -811,6 +821,58 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str):
811
821
812
822
_dijkstra_adjacency_matrix = _dijkstra_adjacency_list
813
823
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
+
814
876
def all_pair_shortest_paths (graph : Graph , algorithm : str ,
815
877
** kwargs ) -> tuple :
816
878
"""
0 commit comments