• A* 算法实现与解析


    【算法分析】
    A*算法适用于
    结点特别多的场景,常借助于优先队列 priority_queue 实现。
    在A*算法的实现中,形如下方的代码,为“
    结构体内嵌比较函数”。之所以这样写,是因为其执行速度快。
    bool operator<(const NODE &a) const {
        return F==a.F?G>a.G:F>a.F;
    }


    下面算法代码执行后,将输出一条如下图所示的绿色最短路径。


     

    A*算法的经典解析可参见:
    https://cloud.tencent.com/developer/article/2040931
    https://blog.csdn.net/m0_46304383/article/details/113457800
    https://blog.csdn.net/dujuancao11/article/details/109749219
    https://www.acwing.com/blog/content/352/
    https://blog.csdn.net/qq_43827595/article/details/99546055


    【算法代码】
    下面代码改编自:https://blog.csdn.net/m0_46304383/article/details/113457800

    1. #include
    2. using namespace std;
    3. const int N=10; //Map order
    4. bool close[N][N]; //Access records, close lists
    5. int valueF[N][N]; //Record the F value of each node
    6. int pre[N][N][2]; //Stores the parent node of each node
    7. int env[N][N]= { //Scene Map
    8. {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    9. {0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
    10. {0, 0, 1, 1, 0, 0, 0, 1, 0, 0},
    11. {0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
    12. {0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
    13. {0, 0, 1, 1, 0, 1, 0, 0, 0, 0},
    14. {0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
    15. {0, 0, 1, 0, 0, 0, 0, 1, 0, 0},
    16. {0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
    17. {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    18. };
    19. struct NODE {
    20. int x,y; //Node location
    21. int F,G,H; //F=G+H
    22. NODE(int a, int b) { //constructor function
    23. x=a,y=b;
    24. }
    25. //struct's nested comparison function,speed is faster.
    26. bool operator<(const NODE &a) const {
    27. return F==a.F?G>a.G:F>a.F;
    28. }
    29. };
    30. //define the direction
    31. //const int ne_pos[8][2]={{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}};
    32. const int ne_pos[4][2]= {{-1,0},{0,1},{1,0},{0,-1}};
    33. priority_queue open;
    34. int Manhattan(int x0,int y0,int x1,int y1) {
    35. return (abs(x0-x1)+abs(y0-y1));
    36. }
    37. bool isValidNode(int x,int y,int u,int v) {
    38. if(x<0||x>=N||y<0||y>=N) return false; //Judge boundary
    39. if(env[x][y]==1) return false; //Judge obstacle
    40. /*When two nodes are diagonal and
    41. their common adjacent nodes have obstacles, return false.
    42. The rule is used when the scene map has 8 directions.*/
    43. if(x!=u && y!=v && (env[x][v]==1 || env[u][y]==1)) return false;
    44. return true;
    45. }
    46. void Astar(int x0,int y0,int x1,int y1) {
    47. NODE node(x0,y0); //Starting point is added into the open list.
    48. node.G=0;
    49. node.H=Manhattan(x0,y0,x1,y1);
    50. node.F=node.G+node.H;
    51. valueF[x0][y0]=node.F;
    52. open.push(node);
    53. while(!open.empty()) {
    54. /*Take the head element of priority queue,
    55. which is the point with the least cost in the surrounding cells*/
    56. NODE cur_node=open.top();
    57. open.pop(); //Remove from the open list
    58. //Access this point and add it to the close list
    59. close[cur_node.x][cur_node.y]=true;
    60. if(cur_node.x==x1 && cur_node.y==y1) break; //Reach the finish line
    61. /*Traverse the four directions around the top_node,
    62. or if the array ne_pos has eight values,
    63. then you need to traverse the eight directions around the top_node.
    64. */
    65. for(int i=0; i<4; i++) {
    66. //Create a point around top_node
    67. NODE ne_node(cur_node.x+ne_pos[i][0],cur_node.y+ne_pos[i][1]);
    68. //The node coordinate is valid and has not been accessed
    69. if(isValidNode(ne_node.x,ne_node.y,cur_node.x,cur_node.y) && !close[ne_node.x][ne_node.y]) {
    70. /*Calculate the cost to reach the node
    71. from the starting point and through the top_node node*/
    72. ne_node.G=cur_node.G+int(sqrt(pow(ne_pos[i][0],2)+pow(ne_pos[i][1],2)));
    73. //Calculate the Manhattan distance from the node to the destination
    74. ne_node.H=Manhattan(ne_node.x,ne_node.y,x1,y1);
    75. /*The estimated cost to reach the destination
    76. from the starting point through the top_node
    77. and the current node*/
    78. ne_node.F=ne_node.G+ne_node.H;
    79. /*ne_node.F
    80. When the condition is met, a better path is found and updated.
    81. valueF[ne_node.x][ne_node.y]==0,
    82. When the condition is met, the node is not added to the OPEN table,
    83. it is needed to be added to the open table*/
    84. if(ne_node.F0) {
    85. //Save the parent node of the node
    86. pre[ne_node.x][ne_node.y][0]=cur_node.x;
    87. pre[ne_node.x][ne_node.y][1]=cur_node.y;
    88. //Modify the valueF value of the node
    89. valueF[ne_node.x][ne_node.y]=ne_node.F;
    90. open.push(ne_node);
    91. }
    92. }
    93. }
    94. }
    95. }
    96. void PrintPath(int x1,int y1) {
    97. if(pre[x1][y1][0]==-1 || pre[x1][y1][1]==-1) {
    98. printf("No path to get.\n");
    99. return;
    100. }
    101. int x=x1,y=y1;
    102. int a,b;
    103. while(x!=-1 || y!=-1) {
    104. env[x][y]=2; //Assign a value of 2 to the node on the feasible path
    105. a=pre[x][y][0];
    106. b=pre[x][y][1];
    107. x=a;
    108. y=b;
    109. }
    110. /*
    111. " " represents an unpassed node,
    112. " #" represents an obstacle,
    113. " @" represents a feasible node.
    114. */
    115. string s[3]= {" "," #", " @"};
    116. for(int i=0; i
    117. for(int j=0; j
    118. cout<//s[] is string, must use cout to output
    119. }
    120. cout<
    121. }
    122. }
    123. int main(){
    124. memset(close,false,sizeof close); //Initialize close array to false
    125. memset(valueF,0,sizeof valueF); //Initialize F to 0
    126. memset(pre,-1,sizeof pre); //Initialize pre array to -1
    127. int x0=2,y0=4; //start point
    128. int x1=8,y1=6; //end point
    129. if(!isValidNode(x0,y0,x0,y0)){
    130. printf("Invalid input.\n");
    131. return 0;
    132. }
    133. Astar(x0,y0,x1,y1); //A* algorithm
    134. PrintPath(x1,y1); //Print the path
    135. return 0;
    136. }
    137. /*
    138. output:
    139. @ @ @
    140. # @ # @
    141. # # @ @ @ # @
    142. # # @
    143. # @
    144. # # # @
    145. # # # @
    146. # # @
    147. # # @ @ @
    148. */


    【未加注释的算法代码】
    鉴于上面代码注释过多,为方便大家快速阅读代码,下面给出的是未加注释版本的算法代码。

    1. #include
    2. using namespace std;
    3. const int N=10;
    4. bool close[N][N];
    5. int valueF[N][N];
    6. int pre[N][N][2];
    7. int env[N][N]= {
    8. {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    9. {0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
    10. {0, 0, 1, 1, 0, 0, 0, 1, 0, 0},
    11. {0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
    12. {0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
    13. {0, 0, 1, 1, 0, 1, 0, 0, 0, 0},
    14. {0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
    15. {0, 0, 1, 0, 0, 0, 0, 1, 0, 0},
    16. {0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
    17. {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    18. };
    19. struct NODE {
    20. int x,y;
    21. int F,G,H;
    22. NODE(int a, int b) {
    23. x=a,y=b;
    24. }
    25. bool operator<(const NODE &a) const {
    26. return F==a.F?G>a.G:F>a.F;
    27. }
    28. };
    29. const int ne_pos[4][2]= {{-1,0},{0,1},{1,0},{0,-1}};
    30. priority_queue open;
    31. int Manhattan(int x0,int y0,int x1,int y1) {
    32. return (abs(x0-x1)+abs(y0-y1));
    33. }
    34. bool isValidNode(int x,int y,int u,int v) {
    35. if(x<0||x>=N||y<0||y>=N) return false;
    36. if(env[x][y]==1) return false;
    37. if(x!=u && y!=v && (env[x][v]==1 || env[u][y]==1)) return false;
    38. return true;
    39. }
    40. void Astar(int x0,int y0,int x1,int y1) {
    41. NODE node(x0,y0);
    42. node.G=0;
    43. node.H=Manhattan(x0,y0,x1,y1);
    44. node.F=node.G+node.H;
    45. valueF[x0][y0]=node.F;
    46. open.push(node);
    47. while(!open.empty()) {
    48. NODE cur_node=open.top();
    49. open.pop();
    50. close[cur_node.x][cur_node.y]=true;
    51. if(cur_node.x==x1 && cur_node.y==y1) break;
    52. for(int i=0; i<4; i++) {
    53. NODE ne_node(cur_node.x+ne_pos[i][0],cur_node.y+ne_pos[i][1]);
    54. if(isValidNode(ne_node.x,ne_node.y,cur_node.x,cur_node.y) && !close[ne_node.x][ne_node.y]) {
    55. ne_node.G=cur_node.G+int(sqrt(pow(ne_pos[i][0],2)+pow(ne_pos[i][1],2)));
    56. ne_node.H=Manhattan(ne_node.x,ne_node.y,x1,y1);
    57. ne_node.F=ne_node.G+ne_node.H;
    58. if(ne_node.F0) {
    59. pre[ne_node.x][ne_node.y][0]=cur_node.x;
    60. pre[ne_node.x][ne_node.y][1]=cur_node.y;
    61. valueF[ne_node.x][ne_node.y]=ne_node.F;
    62. open.push(ne_node);
    63. }
    64. }
    65. }
    66. }
    67. }
    68. void PrintPath(int x1,int y1) {
    69. if(pre[x1][y1][0]==-1 || pre[x1][y1][1]==-1) {
    70. printf("No path to get.\n");
    71. return;
    72. }
    73. int x=x1,y=y1;
    74. int a,b;
    75. while(x!=-1 || y!=-1) {
    76. env[x][y]=2;
    77. a=pre[x][y][0];
    78. b=pre[x][y][1];
    79. x=a;
    80. y=b;
    81. }
    82. string s[3]= {" "," #", " @"};
    83. for(int i=0; i
    84. for(int j=0; j
    85. cout<
    86. }
    87. cout<
    88. }
    89. }
    90. int main() {
    91. memset(close,false,sizeof close);
    92. memset(valueF,0,sizeof valueF);
    93. memset(pre,-1,sizeof pre);
    94. int x0=2,y0=4;
    95. int x1=8,y1=6;
    96. if(!isValidNode(x0,y0,x0,y0)) {
    97. printf("Invalid input.\n");
    98. return 0;
    99. }
    100. Astar(x0,y0,x1,y1);
    101. PrintPath(x1,y1);
    102. return 0;
    103. }
    104. /*
    105. output:
    106. @ @ @
    107. # @ # @
    108. # # @ @ @ # @
    109. # # @
    110. # @
    111. # # # @
    112. # # # @
    113. # # @
    114. # # @ @ @
    115. */



    【参考文献】
    https://cloud.tencent.com/developer/article/2040931
    https://blog.csdn.net/m0_46304383/article/details/113457800
    https://blog.csdn.net/dujuancao11/article/details/109749219
    https://www.acwing.com/blog/content/352/
    https://blog.csdn.net/qq_43827595/article/details/99546055

  • 相关阅读:
    java-php-python-ssm一起组局校园交友平台计算机毕业设计
    【泛微ecology】控制文本框输入汉字数量
    易点易动设备管理系统:提升企业备件管理和维修效率的智能解决方案
    Dubbo源码理解
    2024环境,资源与绿色能源国际会议(ICERGE2024)
    ffmpeg视频编码原理和实战-(4)H264原始码流分析
    BERT深度学习基准模型特点与应用
    顶象无感验证为十八数藏“加固城墙”
    双十一购物指南:电视盒子哪个牌子好?口碑电视盒子品牌排行榜
    【红日靶场】vulnstack1-完整渗透过程
  • 原文地址:https://blog.csdn.net/hnjzsyjyj/article/details/126693357