要学A*算法,首先向大家安利一个视频:一级棒A*算法讲解视频
这个视频真的讲的超级好,演示效果一级棒。以前我看书看A*是真心没看懂,什么启发函数、估值函数...真心看得累。强烈建议和我一样的同学看看上面的那个视频,看完之后,你自己都可以写出A*算法来找最短路径。
然后下面写写我觉得在实现A*算法的过程中,一些需要注意的问题:
1. 注意,已经加入到openList中的Node,需要设置一个标志来记录,下一次访问其他节点的时候,在“把邻居结点加入到openList”这个步骤时,要注意,如果邻居Node已经被加入到openList,则不要重复添加了!之前我忽略了这个问题,结果导致算法在do-while循环中出不来了,因为重复的节点一直在被添加。
2.
注意,GCost = preNode.GCost + GetCost( curNode.pos, PreNode.pos)。
而不是:GCost = GetCost( curNode.pos, startNode.pos)
在修复以上两个Bug后,顺利找到了最短路径。
关于A*,对于找最短路径而言,
HCost = 从当前结点到目标节点的Cost。
GCost = 从当前结点到起始点的Cost。(是cost,不是距离!见上面2)
FCost = HCost + GCost.
部分代码:
1.节点的数据结构
class Node
{
private Node father; //父节点
internal Node Father
{
get { return father; }
set { father = value; }
}
//cost
private int g_cost;
public int G_cost
{
get { return g_cost; }
set { g_cost = value; }
}
private int h_cost;
public int H_cost
{
get { return h_cost; }
set { h_cost = value; }
}
private int f_cost;
public int F_cost
{
get { return f_cost; }
set { f_cost = value; }
}
//对应地图上的行号列号
private int i;
/// <summary>
/// 列号
/// </summary>
public int I
{
get { return i; }
set { i = value; }
}
private int j;
/// <summary>
/// 行号
/// </summary>
public int J
{
get { return j; }
set { j = value; }
}
//是否为墙壁
public bool wall;
//是否被加入到了close中
private bool inClose;
private bool inOpen;
public bool InOpen
{
get { return inOpen; }
set { inOpen = value; }
}
public bool InClose
{
get { return inClose; }
set { inClose = value; }
}
public Node()
{
father = null;
wall = false;
g_cost = 0;
h_cost = 0;
f_cost = 0;
i = 1000;
j = 1000;
inClose = false;
inOpen = false;
}
/// <summary>
/// 外部调用这个函数来给node赋值,初始化时候用
/// </summary>
/// <param name="m">行号</param>
/// <param name="n">列号</param>
/// <param name="wall">map中的数值</param>
public void inital(int m , int n , char iswall)
{
J = m;
I = n;
wall = iswall.Equals('0')?false : true;
inClose = false;
}
}
2.A*算法主体
/// <summary>
/// A*之找到最短路径算法
/// </summary>
/// <param name="map">地图</param>
/// <param name="mapStr">原始txt读到的string,用于最后修改并且打印路径</param>
/// <param name="rowNum">地图的行数</param>
/// <param name="columNum">地图的列数</param>
public void GetShortestPath ( Node[ , ] map , string mapStr , int rowNum , int columNum )
{
//0.将起点加入到开放列表中
openList .Add ( map[ startY_ , startX_ ] );
//int curPosX,curPosY;//temp use,X:列,Y:行
#region do--while循环体
do
{
openList .Sort ( SortByFHcost );
curNode = openList[ 0 ];
//将其从open中移除
openList .Remove ( curNode );
curNode .InOpen = false; ;
//加入到close列表中
//closeList .Add ( curNode );
//设置开关
curNode .InClose = true;
//判断是否为终点
if( curNode.I == endX_ && curNode.J == endY_ )
{
Console .WriteLine ( "find path" );
break;
}
#region 把邻居加入到开放列表中
tempY = curNode .J - 1;
if ( tempY >= 0 )
{
//不越界,检查墙壁,可以则加入
AddNewNodeToList ( map[ tempY , curNode .I ] , curNode );
}
//↖
tempX = curNode .I - 1;
if ( tempX >= 0 && tempY >= 0 )
{
AddNewNodeToList ( map[ tempY , tempX ] , curNode );
}
//↗
//tempY = startY - 1//上面已经算了一次,所以这里可以不运算这一步了
tempX = curNode .I + 1;
if ( tempX < columNum && tempY >= 0 )
{
AddNewNodeToList ( map[ tempY , tempX ] , curNode );
}
//左
tempX = curNode .I - 1;
if ( tempX >= 0 )
{
AddNewNodeToList ( map[ curNode .J,tempX ] , curNode );
}
//右
tempX = curNode .I + 1;
if ( tempX < columNum )
{
AddNewNodeToList ( map[ curNode .J, tempX ] , curNode );
}
//下
tempY = curNode .J + 1;
if ( tempY < rowNum )
{
AddNewNodeToList ( map[ tempY,curNode .I ] , curNode );
}
//↙
tempX = curNode .I - 1;
if ( tempX >= 0 && tempY < rowNum )
{
AddNewNodeToList ( map[ tempY ,tempX ] , curNode );
}
//↘
tempX = curNode .I + 1;
if ( tempX < columNum && tempY < rowNum )
{
AddNewNodeToList ( map[ tempY, tempX ] , curNode );
}
//周围8个全部检查并加入到开放列表完毕
#endregion 加入完毕
}
while( true );
#endregion do-while完毕
//找到最短路径,打印输出
//输出最短路径坐标
}//end for method
效果图:
源代码我放在了GitHub上,需要的朋友:点击打开链接