A*算法的实现(c++优先队列)

A*算法的理解

对于A*算法,网上已经有了很详尽的描述,这里不再重复,如果想去看的话,我看了很多博客,最后看到这个博客的时候成功实现了A*算法
因为详细的说明别人有讲,这里只谈一下实现。
在我看来,A*算法的实质其实就是BFS,只不过BFS的过程中加入了一个变量f,每次选出f最小的点进行BFS,BFS决定了我们能找到最优解,变量f能让我们贪心的更快的找到最优解。

struct node {
    int x, y;
    int f, g, h;// f = g + h;
    int type;
    bool is_open;
    bool is_close;
    node* parent = NULL;
    void print() {
        printf("%d   %d: f: %d  g: %d  h: %d   type: %d\n", x, y, f, g, h, type);
    }
    bool operator < (const node& cmp) const {
        return f > cmp.f;
    }
}map[15][15];

在实现A*算法前,我们需要定义一个结构体node(名字随意起)表示地图的一个点包含的信息,其中x,y表示点坐标,f、g和h是实现A*算法的关键,用来判断BFS的方向,type表示地图当前位置的类型,比如起点,终点或者墙之类的,is_open表示是否在bfs的open优先队列里面,is_close表示是否访问过,不重复访问一个节点。
这时候我们来讲一下怎么计算f,g,h,我们在一开始的时候把起点加入open优先队列里面,这时起点的g=0,起点会向八个方向扩展(当然你也可以写成向四个方向扩展),这时你就需要计算扩展的节点的g,如果是上下左右扩展就+1,向斜扩展就加√2,g的扩展如下:

|g+√2 | g+1 | g+√2|
|  g+1 |  g  | g+1 |
|g+√2 | g+1 | g+√2|

我的话直接将fgh的值扩大10倍取整,就是上下左右+10,斜着+14。h的距离就是扩展的节点与终点之间的哈曼顿距离,假设扩展节点为u,终点为end,这时的h的计算方法为

h = abs(end.x-u.x) + abs(end.y-u.y);

最后计算f = g + h;
我们最后把每次扩展的节点加入open优先队列,并标记节点,不断扩展,直到遇到终点或者open优先队列为空,下面是代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <vector>
#include <time.h>
#include <algorithm>
using namespace std;
//地图
int g[15][15] = {
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 3, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
enum Type {
    Road,Wall,Start,End
    //路,墙,起点,终点
};
int endX = 0, endY = 0;
struct node {
    int x, y;
    int f, g, h;// f = g + h;
    int type;
    bool is_open;
    bool is_close;
    node* parent = NULL;
    void print() {
        printf("%d   %d: f: %d  g: %d  h: %d   type: %d\n", x, y, f, g, h, type);
    }
    bool operator < (const node& cmp) const {
    //这里是给优先队列排序用的,优先队列默认大的在前面,所以我们写<的时候判断用>号
        return f > cmp.f;
    }
}map[15][15];
priority_queue<node> open;
bool inside(int x,int y,int n, int m) {
    return x >= 0 && y >= 0 && x < n && y < m;
}
void A_() {
    while (!open.empty()) {
        node x = open.top();
        printf("%d   %d\n", x.x, x.y);//打印bfs的路径,没有实际作用
        open.pop();
        map[x.x][x.y].is_close = true;
        if (map[endX][endY].is_close) {//从终点走回起点
            int nx = endX, ny = endY;
            while (map[nx][ny].type != Start) {
                node* x = map[nx][ny].parent;
                nx = x->x;
                ny = x->y;
                if (map[nx][ny].type != Start)g[nx][ny] = 6;
            }
            break;
        }
        for (int i = -1; i <= 1; i++)for (int j = -1; j <= 1;j++) {
            if (i == j && i == 0)continue;
            if (!inside(x.x + i, x.y + j, 15, 15))continue;
            if (map[x.x + i][x.y + j].is_close || map[x.x + i][x.y + j].is_open) continue;
            if (map[x.x + i][x.y + j].type == Wall)continue;

            node& u = map[x.x + i][x.y + j];
            u.parent = &(map[x.x][x.y]);
            u.g = map[x.x][x.y].g + ((i&&j) ? 14 : 10);
            u.h = abs(u.x - map[endX][endY].x) + abs(u.y - map[endX][endY].y);
            u.f = u.g + u.f;
            u.is_open = true;

            open.push(map[x.x + i][x.y + j]);
        }
    }
}
void CreateMap() {
    for (int i = 0; i < 15; i++)for (int j = 0; j < 15; j++) {
        map[i][j].x = i;
        map[i][j].y = j;
        map[i][j].type = g[i][j];
        map[i][j].f = map[i][j].g = map[i][j].h = 0;
        map[i][j].is_open = map[i][j].is_close = false;
        if (map[i][j].type == Start) {
            map[i][j].is_open = true;
            open.push(map[i][j]);
        }
        if (map[i][j].type == End) {
            endX = i;
            endY = j;
        }
    }

}
int main()
{

    CreateMap();
    A_();
    for (int i = 0; i < 15; i++) {
        for (int j = 0; j < 15; j++) {
            printf("%d ", g[i][j]);
        }
        puts("");
    }
    return 0;
}
  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
九宫重排问题是一种经典的搜索问题,可以使用A*算法来解决。下面是使用C++实现A*算法解决九宫重排问题的代码示例: ```c++ #include <iostream> #include <queue> #include <vector> #include <map> using namespace std; const int N = 3; // 九宫格大小 const int MAX_STATE = 1e5; // 最大状态数 struct State { int a[N][N]; // 九宫格 int x, y; // 空格位置 int f, g, h; // f = g + h int id; // 状态编号 bool operator < (const State& rhs) const { return f > rhs.f; // 优先队列按 f 值从小到大排序 } }; int dx[4] = {-1, 0, 1, 0}; // 方向数组 int dy[4] = {0, 1, 0, -1}; int start[N][N] = { // 初始状态 {2, 8, 3}, {1, 6, 4}, {7, 0, 5} }; int goal[N][N] = { // 目标状态 {1, 2, 3}, {8, 0, 4}, {7, 6, 5} }; int get_h(const int a[][N]) { // 估价函数 int res = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (a[i][j] == 0) continue; int x = (a[i][j] - 1) / N; int y = (a[i][j] - 1) % N; res += abs(x - i) + abs(y - j); } } return res; } int state_id; // 状态编号 map<int, int> vis; // 判重数组 State S[MAX_STATE]; // 状态数组 int get_id(const int a[][N]) { // 获取状态编号 int res = 0, p = 1; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { res += a[i][j] * p; p *= 10; } } return res; } void print(const State& s) { // 打印状态 for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { cout << s.a[i][j] << " "; } cout << endl; } } bool is_goal(const State& s) { // 判断是否为目标状态 for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (s.a[i][j] != goal[i][j]) return false; } } return true; } void A_star() { // A*算法 priority_queue<State> q; state_id = 0; vis.clear(); State start_state; start_state.x = 1, start_state.y = 2; // 空格位置 for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { start_state.a[i][j] = start[i][j]; } } start_state.g = 0; start_state.h = get_h(start_state.a); start_state.f = start_state.g + start_state.h; start_state.id = state_id++; S[start_state.id] = start_state; q.push(start_state); vis[get_id(start_state.a)] = start_state.id; while (!q.empty()) { State t = q.top(); q.pop(); if (is_goal(t)) { cout << "找到目标状态" << endl; print(t); return; } for (int k = 0; k < 4; k++) { int nx = t.x + dx[k], ny = t.y + dy[k]; if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue; State s = t; s.x = nx, s.y = ny; swap(s.a[t.x][t.y], s.a[nx][ny]); s.h = get_h(s.a); s.g = t.g + 1; s.f = s.g + s.h; int id = get_id(s.a); if (vis.count(id) == 0) { s.id = state_id++; S[s.id] = s; q.push(s); vis[id] = s.id; } else { int pre_id = vis[id]; if (S[pre_id].f > s.f) { S[pre_id] = s; vis[id] = s.id; q.push(s); } } } } } int main() { A_star(); return 0; } ``` A*算法的核心是估价函数,本例中使用的是曼哈顿距离,即当前状态与目标状态每个数码所在位置的曼哈顿距离之和。在代码实现中,使用了优先队列来维护搜索过程中的状态集合,采用了判重和状态编号的方法来优化算法效率。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值