前言
为了弥补之前简陋的JAVA大作业,所以我决定学习一下A* 算法,据说A* 算法的效率 > Dijkstra算法 和 优先搜索(BFS)算法,此BFS非彼BFS希望看到这篇文章的人,能分清楚,当然A*算法的高效之处也是继承了此BFS的高效,但为什么不用此BFS呢,请看正题。
正题
首先我们来引入Dijkstra算法,首先我们明白Dijkstra算法的时间复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的,如果了解Dijkstra算法的,是知道的Dijkstra是盲目的寻找图中的顶点,直到对所有点做了松弛操作后,才算完成搜索,但这样就会利用很多完全不可能的点,这样虽然可以找到最短路,但是效率略低,时间复杂度稳定在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
接下来们引入优先搜索算法(BFS)这个算法的时间复杂度也是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)(指最坏时间复杂度),这个算法其实改良一下就成为A* 了,但是我们为了方便理解我们先讲他的简版,在讲它之前我们引入一个启发函数。
f
(
n
)
f(n)
f(n) 代表 从起点到终点的综合预估花费,
g
(
n
)
g(n)
g(n) 代表起点到
n
n
n 节点的实际花费,
h
(
n
)
h(n)
h(n) 代表从
n
n
n 节点到终点的预估花费。
由于
f
(
n
)
f(n)
f(n) 代表是当前状态最理想的花费,所以我们通过这种启发函数搜索到终点后得到的答案一定是最优的,那么BFS的从广度优先搜索成为优先搜索只需要将队列边为优先队列,每次取预估花费最小的即可,对于网格图的
h
(
n
)
h(n)
h(n) 我们只需要利用欧几里得求出贡献即可。那么这种写法就会避免使用完全没有用的节点,呢么为什么我们不采取呢,主要原因是它的路径不一定是最短的。所以我们不采用这种方法。
那么A* 是如何实现的呢,其实很简单,我们将Dijkstra小改一下,按照优先搜索的写法,并且如果遇到终点直接结束即可,这就是A* 算法,下面我们来比较一下其高效成度。
展示一下A* 网格图寻路的效果图,并且含有遍历点的图,含未被使用的点的概率。
实现代码:
#include <bits/stdc++.h>
using namespace ::std;
const int N = 2e3 + 5;
const int dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
struct Node {
int f, x, y;
bool operator<(const Node& b) const { return f > b.f; }
};
class A_Star {
public:
A_Star(){};
A_Star(int sx, int sy, int ex, int ey, int n, int m)
: sx(sx), sy(sy), ex(ex), ey(ey), n(n), m(m) {}
void scan();
void Ashow();
private:
int sx, sy, ex, ey, n, m;
static int vis[N][N];
static int Map[N][N];
static int dis[N][N];
static int path[N][N][2];
void Dijkstra();
int Dis(int dx, int dy);
};
int A_Star::vis[N][N] = {0};
int A_Star::Map[N][N] = {0};
int A_Star::dis[N][N] = {0};
int A_Star::path[N][N][2] = {0};
int A_Star::Dis(int dx, int dy) {
return dis[dx][dy] + abs(dx - ex) + abs(dy - ey);
}
void A_Star::scan() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
Map[i][j] = 0;
dis[i][j] = 0x3f3f3f3f;
}
}
}
void A_Star::Ashow() {
Dijkstra();
vis[ex][ey] = 3;
int ans = 0;
for (int i = path[ex][ey][0], j = path[ex][ey][1]; i && j;) {
vis[i][j] = 3;
int k1 = path[i][j][0];
int k2 = path[i][j][1];
i = k1, j = k2;
}
// cout << vis[1][3] << endl;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (vis[i][j] == 3)
cout << "*";
else
cout << ".";
}
puts("");
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (vis[i][j])
ans++;
cout << vis[i][j];
}
puts("");
}
cout << endl;
int x = __gcd(n * n, n*n - ans);
cout << (n * n - ans) / x << '/' << n * n / x << endl;
cout << (1.0 * (n * n - ans)) / (n * n * 1.0) << endl;
}
void A_Star::Dijkstra() {
priority_queue<Node> q;
while (!q.empty()) {
q.pop();
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dis[i][j] = 0x3f3f3f3f;
}
}
Node st, en;
dis[sx][sy] = 0;
st.x = sx, st.y = sy;
st.f = Dis(sx, sy);
q.push(st);
while (!q.empty()) {
st = q.top();
q.pop();
if (vis[st.x][st.y])
continue;
vis[st.x][st.y] = 1;
if (st.x == ex && st.y == ey)
return;
for (int i = 0; i < 4; i++) {
int xx = st.x + dir[i][0];
int yy = st.y + dir[i][1];
if (xx <= 0 || yy <= 0 || xx > n || yy > m || Map[xx][yy])
continue;
if (dis[xx][yy] > dis[st.x][st.y] + 1) {
dis[xx][yy] = dis[st.x][st.y] + 1;
path[xx][yy][0] = st.x, path[xx][yy][1] = st.y;
if (!vis[xx][yy]) {
en.x = xx, en.y = yy;
en.f = Dis(xx, yy);
q.push(en);
}
}
}
}
}
int main() {
int n;
cin >> n;
A_Star T(1, 1, n, n, n, n);
T.scan();
T.Ashow();
return 0;
}