前言
A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法。
A*同时也属于启发式搜索算法。下面会对A*展开进行讲解。
一、 静态路网
1. 静态: 指在使用AStar算法寻找路径前,地图属性(格子数量,格子大小, 格子类型等等)不再改变。
2. 路网:全称道路网络。
二 、核心内容
1)、启发式搜索算法
1. 状态空间搜索:
状态空间搜索,专业点的说法就是将问题求解过程表现为从初始状态到目标状态寻找这个路径的过程。由于求解过程中求解条件的不确定性,不完备性,使得有解的路径很多,这就构成了一个抽象的图,这个图指的就是状态空间。问题的求解实际上就是在这个图中找到一条可以由开始走到终点的路径。 这个寻找的过程就是状态空间搜索。
2. 启发式:
启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量毫无价值的搜索路径,提高了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价方法会得到不同的结果。
3. 所以AStar算法就是启发式搜索的一种应用。
2)、启发式估价函数表示:f*(n)=g*(n)+h*(n)
1. f(n): 这个公示用来计算,由初始状态经过状态n到达目标状态的最小消耗预估,在此处可以理解为从起始点走到目标点的最小消耗预估。
2. g(n): 这个公示用来计算,初始状态到达状态n的消耗,在此处可以理解为从起始点到当前位置的消耗,这里的消耗明确已知。
3. h(n): 这个公示用来计算, 从状态n到达目标状态的最小消耗预估,在此处可以理解为由当前点到目标点的最小消耗预估。
3)、曼哈顿距离

1. 图中红线代表曼哈顿距离,绿色代表欧氏距离,也就是直线距离,而蓝色和黄色代表等价的曼哈顿距离。曼哈顿距离——两点在南北方向上的距离加上在东西方向上的距离,即d(i,j)=|xi-xj|+|yi-yj|。
2. 本文中寻路预估消耗启发函数,就采用了曼哈顿距离算法,对其他消耗估计函数感兴趣的同学可自行百度。
4)、代码核心逻辑描述
查找之前必须明确startNode(起始点)和endNode(目标点);
从startNode开始估计消耗,然后遍历相邻点将当前点(此刻为startNode)设置为父节点并预估消耗,更新当前点为邻点中消耗最小的点,开始下一次遍历,直到找到终点。
因为我们都有为节点设置父节点,找到终点后可以倒推出到起始点的路径。
此文章只讨论:上下左右四个方向,不考虑斜向移动。
三、代码实现
1)、Node节点
const cost: number = 1;
interface IPos: {
x: number,
y: number
}
class Node {
public g: number;
public h: number;
public parent: Node;
public pos: IPos;
public type: number;
public get f(): number {
if (this.type == -1) {
return 99999 * cost;
}
return (this.g + this.h) * cost;
}
constructor(pos: Ipos) {
this.pos = pos;
}
}
2)、AStar类
class AStar {
private openList: Node[];
private closeList: Node[];
private start: Node;
private end: Node;
private cur: Node;
public find(start: Node, end: Node): IPos[] {
this.end = end;
this.start = start;
this.end.parent = this.start.parent = null;
this.openList = [];
this.closeList = [];
this.openList.push(this.start);
while(this.openList.length > 0) {
const parent = this.cur = this.openList.shift();
if (this.cur.pos.x == this.end.pos.x && this.cur.pos.y == this.end.pos.y) {
return this.getPath(this.cur);
}
this.closeList.push(this.cur);
this.heuristic(this.cur);
this.mapChild(parent);
}
}
private heuristic(node: Node): void {
const cur = node.pos;
const start = this.start.pos;
const end = this.end.pos;
node.g = Math.abs(cur.x - start.x) + Math.abs(cur.y - start.y);
node.h = Math.abs(cur.x - end.x) + Math.abs(cur.y - end.y);
}
private mapChild(parent: Node): void {
const pos = this.cur.pos;
const up = GetNode({x: pos.x, y: pos.y + 1});
this.checkNode(up);
const down = GetNode({x: pos.x, y: pos.y - 1});
this.checkNode(down);
const left = GetNode({x: pos.x - 1, y: pos.y});
this.checkNode(left);
const right = GetNode({x: pos.x + 1, y: pos.y});
this.checkNode(right);
}
private checkNode(node: Node): void {
const border = 16;
const pos = node.pos;
if (pos.x < 0 || pos.x > border || pos.y < 0 || pos.y > border) {
return;
}
for(let i = 0; i < this.closeList.length; i++) {
const checked = this.closeList[i];
if (pos.x == checked.pos.x && pos.y == checked.pos.y) {
return;
}
}
for (let i = 0; i < this.openList.length; i++) {
const unchecked = this.openList[i];
if (pos.x == unchecked.pos.x && pos.y == unchecked.pos.y) {
return;
}
}
this.openList.push(node);
this.heuristic(node);
this.openList.sort((a,b) => {
return a.f - b.f;
});
}
private getPath(node: Node): IPos[] {
let path = [node.pos];
if (node._parent) {
const parent = node.parent;
path.unshift(...this.getPath(parent));
}
return path;
}
}
四、参考
1. AStar的定义:[百度百科](https://baike.baidu.com/item/A*%E7%AE%97%E6%B3%95)
2. 启发式搜索算法:[百度百科](https://baike.baidu.com/item/启发式搜索算法)
3. 曼哈顿距离:[百度百科](https://baike.baidu.com/item/%E6%9B%BC%E5%93%88%E9%A1%BF%E8%B7%9D%E7%A6%BB)