广度优先遍历,是一种非启发式、不用预处理的寻路算法。通常会将整个图全部遍历,意图寻找终点。(也就是说如果终点位置适宜期一般会遍历整个地图)一般情况,广度优先遍历算法都是基于队列结构,先进先出的遍历所有节点。
该寻路算法,通常先将起点加入队列结构,然后将该点周边符合要求(未加入队列结构并且可以到达的点)的点全部再次加入队列结构中,意图下一次遍历。一直循环该方法。直至全部遍历或者找到终点。
关于广度优先搜索还具有以下性质:
1.其遍历方式走直线不走斜线
2.如果有路必然可以找到路,找到的路也是最短路径
3.需要遍历所有可能通行的节点,如果地图比较大,开销也会比较大
首先从起点开始,每一次搜索这个起点的下一层的所有子节点,第二次通过下一层的所有子节点去搜索第三层的所有的子节点。
通过遍历找到可通行的所有的路径点但没有路径。
这是一幅关于广度遍历规则的一幅图,由图可见,其总体呈菱形结构,如果当这个图足够大时,由于其采用的是横向存储子节点,会导致其内存空间不够用。
下面是深度优先搜索的的C++实现代码:
//深度寻路需要终点才可以寻路
//广度寻路不需要终点就可以寻路
#include "stdafx.h"
#include<vector>
#include<iostream>
using std::cout;
using std::endl;
using std::vector;
#define MAP_ROW 6
#define MAP_COL 8
//准备坐标结构体,表示横坐标和纵坐标
struct Pos
{
int row, col;
};
//准备查找的方向这里采用枚举的方式便于区分后面便于使用switch选择
enum Dir{up,down,left,right};
//准备一个新的地图,这里表示这个新的地图的一个结构体
struct pathNode
{
Dir dir;
int value;
bool isFind;
};
//要进行寻路的地图
int map[MAP_ROW][MAP_COL] = {
{ 0, 0, 0, 1, 0, 0, 0, 0 },
{ 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, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0 },
};
//由于广度优先搜索采用的树搜寻到子节点挂载上,所以这里定义一个树节点的结构体
struct MyTreeNode
{
Pos pos;
MyTreeNode *parent;
vector<MyTreeNode*>children;
};
//判断当前节点是否越界,或者是否已经被遍历过
bool isChecked(pathNode array[][MAP_COL], Pos pos)
{
if (pos.row < 0 || pos.col<0 || pos.row>MAP_ROW || pos.col > MAP_COL)
{
return false;
}
if (array[pos.row][pos.col].value == 1 || array[pos.row][pos.col].isFind == true)
{
return false;
}
else
{
return true;
}
}
void clearTree(MyTreeNode*&pRoot)
{
if (pRoot)
{
for (int i = 0; i < pRoot->children.size(); i++)
{
clearTree(pRoot->children[i]);
}
delete pRoot;
pRoot = nullptr;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//准备到最后用来打印画图的数组
int draw_map[MAP_ROW][MAP_COL] = { 0 };
//为新准备的结构体赋值
pathNode array[MAP_ROW][MAP_COL];
for (int i = 0; i < MAP_ROW; i++)
{
for (int j = 0; j < MAP_COL; j++)
{
array[i][j].dir = up;
array[i][j].isFind = false;
array[i][j].value = map[i][j];
}
}
Pos begin_pos = { 1, 2 };//寻路的起始点
Pos end_pos = { 5,6 };//寻路的终点
array[begin_pos.row][begin_pos.col].isFind = true;//将其实点的isFind赋值为true
//定义根节点
MyTreeNode *pRoot=new MyTreeNode;
pRoot->parent = nullptr;
pRoot->pos = begin_pos;
//准备两个数组用来保存当前节点坐标和下一次进行读取的坐标
vector<MyTreeNode*> NodeList;
vector<MyTreeNode*> NextNodeList;
NodeList.push_back(pRoot);
Pos temp_pos;
//开始寻路操作
while (true)
{
for (int i = 0; i < NodeList.size(); i++)
{
for (int j = 0; j < 4; j++)
{
temp_pos = NodeList[i]->pos;
switch (j)
{
case up:
temp_pos.row--;
break;
case down:
temp_pos.row++;
break;
case left:
temp_pos.col--;
break;
case right:
temp_pos.col++;
break;
}
if (isChecked(array, temp_pos))
{
//如果这个坐标存在,则创建一个新的树节点并将这个坐标赋值给这个树节点,并将这个节点关联。
MyTreeNode *NewNode = new MyTreeNode;
NewNode->parent = NodeList[i];
NewNode->pos = temp_pos;
NodeList[i]->children.push_back(NewNode);
NextNodeList.push_back(NewNode);
array[temp_pos.row][temp_pos.col].isFind = true;
if (temp_pos.row == end_pos.row&&temp_pos.col == end_pos.col)
{
MyTreeNode *tempNode = NewNode;
while (tempNode)
{
///cout << "row:" << tempNode->pos.row << "col:" << tempNode->pos.row << endl;
draw_map[tempNode->pos.row][tempNode->pos.col] = 5;
tempNode = tempNode->parent;
}
goto LoGo;
}
}
}
}
//将下一组节点复制给当前节点,便于进行循环
NodeList = NextNodeList;
NextNodeList.clear();//将之前保存的下一组节点清空,便于其回收利用
}
LoGo:
clearTree(pRoot);
for (int i = 0; i < MAP_ROW; i++)
{
for (int j = 0; j < MAP_COL; j++)
{
if (draw_map[i][j] == 5)
{
cout << " *";
}
else
{
cout << " -";
}
}
cout << endl;
}
system("pause");
return 0;
}