广度寻路算法思想及代码
我的理解:广度寻路算法是以 一颗树根开始,树 不断蔓延至整个地图所有能到达的位置,当树干上的某一片树叶碰到了终点,则到达终点, 因此广度寻路算法是一个寻找最短路径的过程。
位置的点类型
存储每个位置的x y坐标,下面描述的点都是以(row,col)描述,即先行再列
//确定点类型 struct Mypoint { int row; int col; bool operator==(const Mypoint& pos) { //用于判断某个点是否到达终点 return (row == pos.row && col == pos.col); } };
树结构存储整条路线
每个节点都要存储每个位置点坐标,并且这个节点具有父节点和孩子节点,孩子节点可能有多个,例如: 注意:父节点只有一个,因为每个父节点会单独考虑它所拥有的孩子节点
- 用一个容器存储多个孩子节点,每个孩子节点都是TreeNode*类型
//利用树结构存储整条路线 struct TreeNode { Mypoint pos; //存储每个位置点 TreeNode* pParent; //父节点 vector<TreeNode*> pChild; //每个点周围有四个方向,记录四个位置 };
为寻路做准备
-
地图,bool类型辅助地图(判断某个位置有没有走过,走过则为true,走过了则之后不能再走)。
-
规定起始点,终点,试探点(提前试探位置的一个辅助点,帮助看能否移动)。
-
准备一颗树,起始点作为树根。
-
利用数组(容器)存储树的每一层的节点,要有存储当前层和存储下一层两个数组。
-
树根首先进入当前层的容器中
int map[ROW][COL] = { {1,1,1,1,1,1,1,1,1,1}, {1,0,1,1,0,0,0,0,1,1}, {1,0,1,1,0,1,1,1,1,1}, {1,0,1,1,0,1,1,1,1,1}, {1,0,0,0,0,0,0,0,1,1}, {1,0,1,0,1,0,1,0,1,1}, {1,0,1,0,1,0,1,0,1,1}, {1,0,1,0,1,0,1,0,1,1}, {1,0,1,0,1,0,0,0,0,1}, {1,0,0,0,1,1,1,1,1,1}, }; //辅助地图存储某一个位置是否已经走过 bool pathMap[ROW][COL] = { 0 }; //一开始都没走过 //起始点 Mypoint Begpos{ 1,1 }; Mypoint Endpos{ 8,8 }; Mypoint searchpos; //试探点 //准备一棵树 起点当作根节点 TreeNode* pRoot = Create_TreeNode(Begpos); TreeNode* pTemp = NULL; //利用数组存储树的每一层的节点 vector<TreeNode*> CurrentRow; //当前层节点 vector<TreeNode*> NextRow; //下一层节点 //起始点存入容器 CurrentRow.push_back(pRoot);
广度寻路过程
- 每一层存储可以移动的节点,从第一层开始依次遍历每个节点
- 每个节点都有四个方向,再依次遍历四个方向
- 试探点设置为当前点坐标
- 试探点的row col根据方向依次发生变化
- 判断移动后的试探点的位置是否是能够正确移动的位置,即该点没有越界,不是墙壁,没有走过
- 如果在此方向可以移动,则创建一个树节点pTemp,pTemp记录者位置的坐标,pTemp成为当前点的孩子,当前点成为pTemp的父亲,pTemp存入下一层。
- 当前层移动到下一层,重新开始遍历新的一层,重复执行步骤1
- 当有多条路径时,如果有一条最短的路径到达了终点,则循环停止,可以获取这条最短的路径。
注意:
- CurrentRow.size() 容器成员函数,返回当前层共有多少个节点,依次进行操作
- 对每个节点再进行4个方向的遍历移动
- 如果某个方向可以移动,则移动到该点,并创建该点的树节点,确定父子关系
- 每次遍历一层,并存储下一层的节点,如果到达死胡同,则存储下一层节点的容器为空,则退出,我们提前结束到达终点的情况,不依赖此情况
while (1) { NextRow.clear(); //循环一次之后,刷新存储下一层的数组 //依次操作每一层的节点 for (int i = 0; i < CurrentRow.size(); i++) { //标记当前点走过 pathMap[CurrentRow[i]->pos.row][CurrentRow[i]->pos.col] = true; //对于每个节点都有4个方向 for (int j = 0; j < NUM; j++) { //试探点就是当前点位置 searchpos = CurrentRow[i]->pos; switch (j) { case p_up:searchpos.row--; break; case p_right:searchpos.col++; break; case p_down:searchpos.row++; break; case p_left:searchpos.col--; break; } //某个方向可以移动 if (IsCanWalk(map,pathMap,searchpos)) { //创建新的树节点 pTemp = Create_TreeNode(searchpos); //入树 //pTemp称为当前点的孩子 CurrentRow[i]->pChild.push_back(pTemp); //当前点成为pTemp的父亲 pTemp->pParent = CurrentRow[i]; //pTemp存入下一层数组中 NextRow.push_back(pTemp); //判断是否是终点 if (pTemp->pos == Endpos) { isFindEnd = true; goto jmp; } } } } if (NextRow.size() == 0) { //地图遍历完了还是没有终点 break; } //当前层移动到下一层 CurrentRow = NextRow; }
完整代码
#include <iostream> #include <string> #include <cstdlib> #include <vector> using namespace std; #define ROW 10 #define COL 10 //最多四个方向 #define NUM 4 enum Dir { p_up, p_right, p_left, p_down }; //确定点类型 struct Mypoint { int row; int col; bool operator==(const Mypoint& pos) { return (row == pos.row && col == pos.col); } }; //利用树结构存储整条路线 struct TreeNode { Mypoint pos; //存储每个位置点 TreeNode* pParent; //父节点 vector<TreeNode*> pChild; //每个点周围有四个方向,记录四个位置 }; //创建树节点 TreeNode* Create_TreeNode(const Mypoint& pos); //判断四个方向的某个位置能不能走 bool IsCanWalk(int map[ROW][COL], bool pathMap[ROW][COL], Mypoint pos); int main() { int map[ROW][COL] = { {1,1,1,1,1,1,1,1,1,1}, {1,0,1,1,0,0,0,0,1,1}, {1,0,1,1,0,1,1,1,1,1}, {1,0,1,1,0,1,1,1,1,1}, {1,0,0,0,0,0,0,0,1,1}, {1,0,1,0,1,0,1,0,1,1}, {1,0,1,0,1,0,1,0,1,1}, {1,0,1,0,1,0,1,0,1,1}, {1,0,1,0,1,0,0,0,0,1}, {1,0,0,0,1,1,1,1,1,1}, }; //辅助地图存储某一个位置是否已经走过 bool pathMap[ROW][COL] = { 0 }; //一开始都没走过 //起始点 Mypoint Begpos{ 1,1 }; Mypoint Endpos{ 8,8 }; Mypoint searchpos; //试探点 //准备一棵树 起点当作根节点 TreeNode* pRoot = Create_TreeNode(Begpos); TreeNode* pTemp = NULL; //利用数组存储树的每一层的节点 vector<TreeNode*> CurrentRow; //当前层节点 vector<TreeNode*> NextRow; //下一层节点 //起始点存入容器 CurrentRow.push_back(pRoot); bool isFindEnd = false; while (1) { NextRow.clear(); //依次操作每一层的节点 for (int i = 0; i < CurrentRow.size(); i++) { //标记当前点走过 pathMap[CurrentRow[i]->pos.row][CurrentRow[i]->pos.col] = true; //对于每个节点都有4个方向 for (int j = 0; j < NUM; j++) { //试探点就是当前点位置 searchpos = CurrentRow[i]->pos; switch (j) { case p_up:searchpos.row--; break; case p_right:searchpos.col++; break; case p_down:searchpos.row++; break; case p_left:searchpos.col--; break; } //某个方向可以移动 if (IsCanWalk(map,pathMap,searchpos)) { //创建新的树节点 pTemp = Create_TreeNode(searchpos); //入树 //pTemp称为当前点的孩子 CurrentRow[i]->pChild.push_back(pTemp); //当前点成为pTemp的父亲 pTemp->pParent = CurrentRow[i]; //pTemp存入下一层数组中 NextRow.push_back(pTemp); //判断是否是终点 if (pTemp->pos == Endpos) { isFindEnd = true; goto jmp; } } } } if (NextRow.size() == 0) { //地图遍历完了还是没有终点 break; } //当前层移动到下一层 CurrentRow = NextRow; } jmp:; if (isFindEnd) { cout << "到达终点!\n"; while (pTemp) { cout << "(" << pTemp->pos.row << "," << pTemp->pos.col << ")" << " "; pTemp = pTemp->pParent; } } system("pause"); return 0; } //创建树节点 TreeNode* Create_TreeNode(const Mypoint& pos) { TreeNode* pNew = new TreeNode; if (!pNew) { cerr << "节点创建失败!\n"; exit(-1); } //元素重置为零 memset(pNew, 0, sizeof(TreeNode)); pNew->pos = pos; //把能够移动的点位置存储起来 return pNew; } //判断四个方向的某个位置能不能走 bool IsCanWalk(int map[ROW][COL], bool pathMap[ROW][COL], Mypoint pos) { //首先判断越界 if (pos.row < 0 || pos.row >= ROW || pos.col < 0 || pos.col >= COL) { return false; } //墙壁 if (map[pos.row][pos.col] == 1) { return false; } //走过的不能再走 if (pathMap[pos.row][pos.col]) { return false; } return true; }
本文来自博客园,作者:hugeYlh,转载请注明原文链接:https://www.cnblogs.com/helloylh/p/17209740.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)