Astar算法 A星算法

​​​​​​​​​​​​​https://blog.csdn.net/jj6666djdbbd/article/details/126287486?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169261313816800213024970%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169261313816800213024970&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-126287486-null-null.142^v93^chatgptT3_1&utm_term=a%E6%98%9F%E7%AE%97%E6%B3%95&spm=1018.2226.3001.4187

 

前几天跟着这个教程学了A星算法,发现如果遇到规模大且复杂的地图,识别速度会非常慢,个人认为是一个格子会被周围八个点进行反复搜索导致的,因此改进了算法,识别效率大幅提升,还做了个可视化,大家可以参考

#include<vector>
#include <iostream>
#include<cstring>
#include<stdlib.h>
#include<Windows.h>

#define ROWS 20  //二十行二十列
#define COLS 20
#define ZXDJ 10  //直线和斜线代价  
#define XXDJ 14

enum dirent { p_up, p_dowm, p_left, p_right, p_lup, p_rup, p_ldowm, p_rdowm }; //枚举

using namespace std;

//定义地图上的点
struct point
{
    int x=0, y=0;           //数组下标,表示地图中位置
    int f=0, g=0, h=0;      //量化评估
};

struct treeNode {           //树的节点
    point pos;
    vector<treeNode*> child;    //父有多子
    treeNode* pParent = NULL;   //子有一父 
};

//创建树节点的函数
treeNode* createTreeNode(int x, int y)
{
    treeNode* pNew = new treeNode;
    treeNode tlist = { 0 };  //初始化,将内存全部赋值为0,即NULL

    pNew->pos.x = x;
    pNew->pos.y = y;
    return pNew;
}

void FindAroundPoint(int i, treeNode* pChild)
{
    switch (i)
    {
    case p_up:
        if (pChild->pos.x == 0)
        {
            break;
        }
        pChild->pos.x--;    //如果往上走,x-1
        pChild->pos.g += ZXDJ;
        break;
    case p_dowm:
        if (pChild->pos.x == 19)
        {
            break;
        }
        pChild->pos.x++;    //如果往下走,x+1
        pChild->pos.g += ZXDJ;
        break;
    case p_left:
        if (pChild->pos.y == 0)
        {
            break;
        }
        pChild->pos.y--;    //如果往左走,y-1
        pChild->pos.g += ZXDJ;
        break;
    case p_right:
        if (pChild->pos.y == 19)
        {
            break;
        }
        pChild->pos.y++;    //如果往右走,y+1
        pChild->pos.g += ZXDJ;
        break;
    case p_lup:
        if (pChild->pos.x == 0 ||pChild->pos.y == 0)
        {
            break;
        }
        pChild->pos.x--;    //如果往左上走,x-1,y-1
        pChild->pos.y--;
        pChild->pos.g += XXDJ;
        break;
    case p_rup:
        if (pChild->pos.x == 0 || pChild->pos.y == 19)
        {
            break;
        }
        pChild->pos.x--;    //如果往右上走,x-1,y+1
        pChild->pos.y++;
        pChild->pos.g += XXDJ;
        break;
    case p_ldowm:
        if (pChild->pos.x == 19 || pChild->pos.y == 0)
        {
            break;
        }
        pChild->pos.x++;    //如果往左下走,x+1,y-1
        pChild->pos.y--;
        pChild->pos.g += XXDJ;
        break;
    case p_rdowm:
        if (pChild->pos.x == 19 || pChild->pos.y == 19)
        {
            break;
        }
        pChild->pos.x++;    //如果往右下走,x+1,y+1
        pChild->pos.y++;
        pChild->pos.g += XXDJ;
        break;
    }
}

//计算当前点与终点的曼哈顿距离
int getH(point pos, point endPos) 
{
    int x, y;

    if (endPos.x > pos.x)
        { x = endPos.x - pos.x; }
    else{ x = pos.x - endPos.x; }

    if (endPos.y > pos.y)
        { y = endPos.y - pos.y; }
    else { y = pos.y - endPos.y; }

    return ZXDJ * (x + y);
}

void print3(int map[ROWS][COLS], int row, int col, point begin, point end ,treeNode *pCurrent) //在cmd中输出路径
{
    int i = 0;
    int j = 0;

    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            // cmd该颜色句柄
            HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);  
            // 获取当前文本属性
            CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
            GetConsoleScreenBufferInfo(hConsole, &consoleInfo);
            WORD originalAttributes = consoleInfo.wAttributes;

            if (map[i][j] == 1)    //墙
            {
                SetConsoleTextAttribute(hConsole,BACKGROUND_BLUE | FOREGROUND_INTENSITY);  //蓝墙
            }

            if (map[i][j] == 2)    //搜索过的点
            {
                // 设置新的文本属性,可以选择以下颜色组合
                // FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_BLUE,
                // FOREGROUND_INTENSITY, BACKGROUND_RED, BACKGROUND_GREEN,
                // BACKGROUND_BLUE, BACKGROUND_INTENSITY
                SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY);    //红字
            }

            if (map[i][j] == 3)     //最终路径
            {
                SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY);  //绿字
            }

            if ((begin.x == i && begin.y == j) || (end.x == i && end.y == j))    //起点终点
            {
                SetConsoleTextAttribute(hConsole, BACKGROUND_GREEN | BACKGROUND_INTENSITY);   //浅蓝墙
            }

            printf("%d ", map[i][j]);

            // 恢复原始文本属性
            SetConsoleTextAttribute(hConsole, originalAttributes);
        }
        printf("\n");
    }
}

void IsFindEnd(bool isFindEnd, int map[ROWS][COLS], point begPos, point endPos, treeNode* pCurrent)
{
    if (isFindEnd)
    {
        printf("找到终点\n");
        printf("路径");
        while (pCurrent)   //打印路径的点
        {
            printf("(%d,%d)", pCurrent->pos.x, pCurrent->pos.y);
            map[pCurrent->pos.x][pCurrent->pos.y] = 3;  //最终路径会在可视化时显示3
            pCurrent = pCurrent->pParent;  //往上走,子走到父
        }
        printf("\n");
    }
    else
    {
        printf("未找到终点\n");   
    }
    print3(map, ROWS, COLS, begPos, endPos, pCurrent); //可视化
}

int main()
{
    int map[ROWS][COLS] = {
        { 0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 1,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 }, //4
        { 0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, //8
        { 0,1,1,1,1,1,1,1,0,0,1,0,0,1,1,1,1,1,0,0 },
        { 0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,1,0,0 },
        { 0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,0,1,1,1 },
        { 1,0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0 }, //12
        { 1,0,0,0,0,1,0,1,1,1,1,0,0,0,0,1,0,0,0,0 },
        { 1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 1,0,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0 }, //16
        { 1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
        { 1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
        { 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0 },
        };

    point begPos = { 0,0 };
    point endPos = { 8,8 };

    bool pathMap[ROWS][COLS] = { 0 }; //走过的地图,虚拟地图,全false,走过标记1(true)
     
    //  准备一棵树,从起点开始,循环寻路,找出周围能走的点,入树,入容器,
    //  挑出数组中f最小的点,走,遇终点或没终点:停,显示路径
    

    treeNode* pRoot = createTreeNode(begPos.x, begPos.y);  //起点是树的根节点
    pathMap[begPos.x][begPos.y] = true;                    //虚拟地图里标记起点已经走过
    
    treeNode* pCurrent = pRoot;      //当前点
    treeNode* pChild = NULL;         //根节点的孩子
    treeNode* tmp3 = NULL;           //临时节点
    
    //准备一个数组,用来存周围一圈的点
    vector<treeNode*>buff;
    vector<treeNode*>::iterator it;             //变化的迭代器
    vector<treeNode*>::iterator itMin;          //记录最小的
    vector<treeNode*>::iterator tmp2;

    bool isFindEnd = false;

    int o = 0;  //用来打印走了几次
    int k = 0;  //用来判断搜索到的点是否在buff存在,是否需要计算

    while (1)
    {
        //找出周围能走的点8个
        for (int i = 0; i < 8; i++)
        {

            pChild = createTreeNode(pCurrent->pos.x, pCurrent->pos.y);  //创建出当前点的孩子

            FindAroundPoint(i, pChild); //找到周围八个点Pchild

            //检查pChild是否能走 ( 是否是障碍物 或 已走过的点 或超过边界)
            if (map[pChild->pos.x][pChild->pos.y] == 0 &&
                pathMap[pChild->pos.x][pChild->pos.y] == false)
            {
                if (k != 0)  //第一次第一个搜索不拦截
                {
                    tmp2 = buff.begin();
                    for (; tmp2 != buff.end(); tmp2++)
                    {
                        tmp3 = *tmp2;
                        if (pChild->pos.x == tmp3->pos.x && pChild->pos.y == tmp3->pos.y)  //如果搜索的,在buff里已经存在了,拦下来
                        {
                            k = 2;
                            delete pChild;
                        }
                    }
                }

                if (k != 2)
                {
                    //计算h值
                    pChild->pos.h = getH(pChild->pos, endPos);

                    //计算f值
                    pChild->pos.f = pChild->pos.g + pChild->pos.h;

                    //入树
                    pCurrent->child.push_back(pChild);   //老节点把新节点包括在内,新节点成为老节点孩子
                    pChild->pParent = pCurrent;          //老节点成为新节点的父亲

                    //入容器
                    buff.push_back(pChild);              //可走的路都进来了,然后再选取最小,走了就把最小的删去   
                }
                k = 1;
            }
            else
            {
                delete pChild;  //不能走,释放pChild
            }
        }

        int buffSize = buff.size();
        printf("buffSize = %d ", buffSize);   

        //数组中挑出 f 最小的点
        //假设a最小,循环让b、c、d、e来跟a比较,如果c最小,让itMin等于c
        it = buff.begin();
        itMin = buff.begin();

        for (; it != buff.end(); it++)
        {
            itMin = ((*itMin)->pos.f < (*it)->pos.f) ? itMin : it;
        }

        //走
        pCurrent = *itMin;
        pathMap[pCurrent->pos.x][pCurrent->pos.y] = true;      //标记走过的点
        map[pCurrent->pos.x][pCurrent->pos.y] = 2;             //走过的路=2

        printf("\n");
        print3(map, ROWS, COLS, begPos, endPos, pCurrent);     //可视化
        printf("走了%d次\n", o);
        o++;
 
        Sleep(50);
        system("cls");
        
        //遇到终点或走完地图没终点,结束
        if (endPos.x == pCurrent->pos.x && endPos.y == pCurrent->pos.y)
        {
            isFindEnd = true;
            break;
        }
        if (buff.size() == 0)
        {
            break;
        }

        buff.erase(itMin);  //删掉走过的点,不然会拿到下一次做比较,遇到死胡同会回路,先判断完再删掉,不然只有一个走了后删了,就没了
    }

    IsFindEnd(isFindEnd, map, begPos, endPos, pCurrent);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值