我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何用C++实现A*寻路算法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何用C++实现A*寻路算法

一、A*算法介绍

寻路,即找到一条从某个起点到某个终点的可通过路径。而因为实际情况中,起点和终点之间的直线方向往往有障碍物,便需要一个搜索的算法来解决。

有一定算法基础的同学可能知道从某个起点到某个终点通常使用深度优先搜索(DFS),DFS搜索的搜索方向一般是8个方向(如果不允许搜索斜向,则有4个),但是并无优先之分。

为了让DFS搜索更加高效,结合贪心思想,我们给搜索方向赋予了优先级,直观上离终点最近的方向(直观上的意思是无视障碍物的情况下)为最优先搜索方向,这就是A*算法。

二、A*算法步骤解析

(如下图,绿色为起点,红色为终点,蓝色为不可通过的墙。)

从起点开始往四周各个方向搜索。

(这里的搜索方向有8个方向)

为了区分搜索方向的优先级,我们给每个要搜索的点赋予2个值。

G值(耗费值):指从起点走到该点要耗费的值。

H值(预测值):指从该点走到终点的预测的值(从该点到终点无视障碍物情况下预测要耗费的值,也可理解成该点到终点的直线距离的值)

在这里,值 = 要走的距离

(实际上,更复杂的游戏,因为地形不同(例如陷阱,难走的沙地之类的),还会有相应不同的权值:值 = 要走的距离 * 地形权值)

我们还定义直着走一格的距离等于10,斜着走一格的距离等于14(因为45°斜方向的长度= sqrt(10^2+10^2) ≈ 14)

F值(优先级值):F = G + H

这条公式意思:F是从起点经过该点再到达终点的预测总耗费值。通过计算F值,我们可以优先选择F值最小的方向来进行搜索。

(每个点的左上角为F值,左下角为G值,右下角为H值)

计算出每个方向对应点的F,G,H值后,

还需要给这些点赋予当前节点的指针值(用于回溯路径。因为一直搜下去搜到终点后,如果没有前一个点的指针,我们将无从得知要上次经过的是哪个点,只知道走到终点最终耗费的最小值是多少)

然后我们将这些点放入openList(开启列表:用于存放可以搜索的点)

然后再将当前点放入closeList(关闭列表:用于存放已经搜索过的点,避免重复搜索同一个点)

然后再从openList取出一个F值最小(最优先方向)的点,进行上述同样的搜索。

在搜索过程中,如果搜索方向上的点是障碍物或者关闭列表里的点,则跳过之。

通过递归式的搜索,多次搜索后,最终搜到了终点。

搜到终点后,然后通过前一个点的指针值,我们便能从终点一步步回溯通过的路径点。

(红色标记了便是回溯到的点)

三、A*算法优化思路

3.1、openList使用优先队列(二叉堆)

可以看到openlist(开启列表),需要实时添加点,还要每次取出最小值的点。

所以我们可以使用优先队列(二叉堆)来作为openList的容器。

优先队列(二叉堆):插入一个点的复杂度为O(logN),取出一个最值点复杂度为O(logN)

3.2、障碍物列表,closeList 使用二维表(二维数组)

由于障碍物列表和closeList仅用来检测是否能通过,所以我们可以使用bool二维表来存放。


//假设已经定义Width和Height分别为地图的长和宽
bool barrierList[Width][Height];
bool closetList[Width][Height];

有某个点(Xa,Yb),可以通过

if(barrierList[Xa][Yb]&&closeList[Xa][Yb])来判断。

因为二维表用下标访问,效率很高,但是耗空间比较多。(三维地图使用三维表则更耗内存。不过现在计算机一般都不缺内存空间,所以尽量提升运算时间为主)

这是一个典型的牺牲内存空间换取运算时间的例子。

3.3、深度限制

有时要搜的路径非常长,利用A*算法搜一次付出的代价很高,造成游戏的卡顿。

那么为了保证每次搜索不会超过一定代价,可以设置深度限制,每搜一次则深度+1,搜到一定深度限制还没搜到终点,则返还失败值。

四、A*算法实现(C++代码)


#include <iostream>
#include <list>
#include <vector>
#include <queue>

struct OpenPoint{
    int x;
    int y;
    int cost;                 // 耗费值
    int pred;                 // 预测值
    OpenPoint* father;        // 父节点
    OpenPoint() = default;
    OpenPoint(int pX,int pY, int endX, int endY, int c, OpenPoint* fatherp) : x(pX),y(pY),cost(c), father(fatherp) {
        //相对位移x,y取绝对值
        int relativeX = std::abs(endX - pX);
        int relativeY = std::abs(endY - pY);
        //x,y偏移值n
        int n = relativeX - relativeY;
        //预测值pred = (max–n)*14+n*10+c
        pred = std::max(relativeX, relativeY) * 14 - std::abs(n) * 4 + c;
    }
};

//比较器,用以优先队列的指针类型比较
struct OpenPointPtrCompare {
    bool operator()(OpenPoint* a, OpenPoint* b) {
        return a->pred > b->pred;
    }
};

const int width = 30;            //地图长度
const int height = 100;          //地图高度
char mapBuffer[width][height];   //地图数据
int depth = 0;                   //记录深度
const int depthLimit = 2000;     //深度限制
bool closeAndBarrierList[width][height];    //记录障碍物+关闭点的二维表
//八方的位置
int direction[8][2] = { {1,0},{0,1},{-1,0},{0,-1},{1,1},{ -1,1 },{ -1,-1 },{ 1,-1 } };
//使用最大优先队列
std::priority_queue<OpenPoint*, std::vector<OpenPoint*>, OpenPointPtrCompare> openlist;
//存储OpenPoint的内存空间
std::vector<OpenPoint> pointList = std::vector<OpenPoint>(depthLimit);

//是否在障碍物或者关闭列表
inline bool inBarrierAndCloseList(int pX,int pY) {
    if (pX < 0 || pY < 0 || pX >= width || pY >= height)
        return true;
    return closeAndBarrierList[pX][pY];
}

//创建一个开启点
inline OpenPoint* createOpenPoint(int pX,int pY,int endX,int endY, int c, OpenPoint* fatherp) {
    pointList.emplace_back(pX,pY,endX,endY, c, fatherp);
    return &pointList.back();
}

// 开启检查,检查父节点
void open(OpenPoint& pointToOpen, int endX,int endY) {
    //将父节点从openlist移除
    openlist.pop();
    //深度+1
    depth++;
    //检查p点八方的点
    for (int i = 0; i < 4; ++i)
    {
        int toOpenX = pointToOpen.x + direction[i][0];
        int toOpenY = pointToOpen.y + direction[i][1];
        if (!inBarrierAndCloseList(toOpenX,toOpenY)) {
            openlist.push(createOpenPoint(toOpenX, toOpenY, endX,endY, pointToOpen.cost + 10, &pointToOpen));
        }
    }
    for (int i = 4; i < 8; ++i)
    {
        int toOpenX = pointToOpen.x + direction[i][0];
        int toOpenY = pointToOpen.y + direction[i][1];
        if (!inBarrierAndCloseList(toOpenX, toOpenY)) {
            openlist.push(createOpenPoint(toOpenX, toOpenY, endX, endY, pointToOpen.cost + 14, &pointToOpen));
        }
    }
    //最后移入closelist
    closeAndBarrierList[pointToOpen.x][pointToOpen.y] = true;
}

//开始搜索路径
std::list<OpenPoint*> findway(int startX,int startY, int endX,int endY) {
    std::list<OpenPoint*> road;
    // 创建并开启一个父节点
    openlist.push(createOpenPoint(startX,startY, endX,endY, 0, nullptr));
    OpenPoint* toOpen = nullptr;
    //重复寻找预测和花费之和最小节点开启检查
    while (!openlist.empty())
    {
        toOpen = openlist.top();
        // 找到终点后,则停止搜索
        if (toOpen->x == endX && toOpen->y ==endY) {break;}//若超出一定深度(1000深度),则搜索失败
        if (depth >= depthLimit) {
            toOpen = nullptr;
            break;
        }
        open(*toOpen, endX,endY);
    }
    for (auto rs = toOpen; rs != nullptr; rs = rs->father) {road.push_back(rs);}
    return road;
}

//创建地图
void createMap() {
    for (int i = 0; i < width; ++i)
        for (int j = 0; j < height; ++j) {
            //五分之一概率生成障碍物,不可走
            if (rand() % 5 == 0) {
                mapBuffer[i][j] = '*';
                closeAndBarrierList[i][j] = true;
            }
            else {
                mapBuffer[i][j] = ' ';
                closeAndBarrierList[i][j] = false;
            }
        }
}

//打印地图
void printMap() {
    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j)
            std::cout << mapBuffer[i][j];
        std::cout << std::endl;
    }
    std::cout << std::endl << std::endl << std::endl;
}

int main() {
    //起点
    int beginX = 0;
    int beginY = 0;
    //终点
    int endX = 29;
    int endY = 99;
    //创建地图
    createMap();
    //保证起点和终点都不是障碍物
    mapBuffer[beginX][beginY] = mapBuffer[endX][endY] = ' ';
    closeAndBarrierList[beginX][beginY] = closeAndBarrierList[endX][endY] = false;
    //A*搜索得到一条路径
    std::list<OpenPoint*> road = findway(beginX,beginY,endX,endY);
    //将A*搜索的路径经过的点标记为'O'
    for (auto& p : road){mapBuffer[p->x][p->y] = 'O';}
    //打印走过路后的地图
    printMap();
    system("pause");
    return 0;
}

示例效果:

以上就是如何用C++实现A*寻路算法的详细内容,更多关于C++ A*寻路算法的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何用C++实现A*寻路算法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

python实现A*寻路算法

目录A* 算法简介关键代码介绍保存基本信息的地图类搜索到的节点类算法主函数介绍代码的初始化完整代码A* 算法简介 A* 算法需要维护两个数据结构:OPEN 集和 CLOSED 集。OPEN 集包含所有已搜索到的待检测节点。初始状态,OPEN
2022-06-02

C++ DFS算法如何实现走迷宫自动寻路

小编给大家分享一下C++ DFS算法如何实现走迷宫自动寻路,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!C++ DFS算法实现走迷宫自动寻路,供大家参考,具体内容
2023-06-15

C#中Astar寻路算法怎么实现

以下是一种基本的A*寻路算法的实现示例,可以用于C#语言:```csharpusing System;using System.Collections.Generic;public class Node{public int X { get
2023-09-22

java寻路算法怎么实现

Java中的寻路算法可以使用图的搜索算法来实现。以下是一个简单的示例,使用BFS(广度优先搜索)算法来寻找路径。```javaimport java.util.*;public class PathFinding {// 定义图的大小pri
2023-09-22

C# AStar寻路算法怎么使用

这篇文章主要讲解了“C# AStar寻路算法怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C# AStar寻路算法怎么使用”吧!概述AStar算法是一种图形搜索算法,常用于寻路。他是
2023-07-05

C++最短路径Dijkstra算法如何实现

这篇文章主要介绍“C++最短路径Dijkstra算法如何实现”,在日常操作中,相信很多人在C++最短路径Dijkstra算法如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++最短路径Dijkstra
2023-07-05

Java编程如何实现A*算法

这篇文章主要介绍了Java编程如何实现A*算法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。本文实例代码结构:% % % % % % % % o o o o o % %
2023-05-30

unity如何实现自带寻路导航系统

小编给大家分享一下unity如何实现自带寻路导航系统,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、介绍unity官方文档:导航网格(即 Navigation
2023-06-25

如何使用C/C++实现马踏棋盘算法

这篇文章主要介绍如何使用C/C++实现马踏棋盘算法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体内容如下问题描述:将马随机放在国际象棋的8×8棋盘Board[0~7][0~7]的某个方格中,马按走棋规
2023-06-29

C++如何实现并查集算法

这篇文章主要介绍了C++如何实现并查集算法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1、并查集的初始化并查集是用一个数组实现的。首先先定义一个数组:int father[
2023-06-29

C#如何实现银行家算法

这篇文章给大家分享的是有关C#如何实现银行家算法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.死锁死锁,顾名思义,是一种锁住不可自行解开的死局。在操作系统中,“死锁”用于描述资源分配时,进程互相抢占资源,又因
2023-06-15

C#如何实现抢红包算法

今天小编给大家分享一下C#如何实现抢红包算法的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。二倍均值法(公平版) 发出一个固定
2023-06-29

C++ opencv如何利用grabCut算法实现抠图

今天小编给大家分享一下C++ opencv如何利用grabCut算法实现抠图的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。前
2023-06-30

c语言如何实现排序算法

小编给大家分享一下c语言如何实现排序算法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.选择排序-简单选择排序选择排序是最简单的一种基于O(n2)时间复杂度的排
2023-06-15

C++如何实现希尔排序算法

这篇“C++如何实现希尔排序算法”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C++如何实现希尔排序算法”文章吧。1.代码模
2023-06-26

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录