我的理解:广度寻路算法是以 一颗树根开始,树 不断蔓延至整个地图所有能到达的位置,当树干上的某一片树叶碰到了终点,则到达终点, 因此广度寻路算法是一个寻找最短路径的过程。
位置的点类型
存储每个位置的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;
}