DFS(深度优先搜索)和BFS(广度优先搜索)是两种常用的图遍历算法,它们在搜索图或树中的节点时有着不同的策略和特点。
-
深度优先搜索 (DFS):
- 在DFS中,从起始节点开始,沿着一条路径尽可能深地搜索,直到到达叶子节点或者无法继续搜索为止,然后回溯到上一个节点,选择另一条路径继续搜索,直到所有节点都被访问。
- DFS通常使用递归或者栈来实现,递归是自然而直观的实现方式,而使用栈可以避免递归的潜在问题(如栈溢出)。
- DFS的优点是在搜索过程中不需要记录所有访问过的节点,因此占用的空间较少。
- 适用于寻找深度路径,比如解决迷宫问题、寻找图的连通分量等。
-
广度优先搜索 (BFS):
- 在BFS中,从起始节点开始,首先访问起始节点的所有邻居节点,然后依次访问这些邻居节点的邻居节点,以此类推,直到所有节点都被访问。
- BFS通常使用队列来实现,保证了节点的访问顺序是按照距离起始节点的距离逐层递增的。
- BFS的优点是可以找到起始节点到目标节点的最短路径,而且在无权图中具有最优性(即找到的第一个解就是最短路径)。
- 适用于寻找最短路径,比如在迷宫中找到最短路径、在社交网络中找到两个人之间的最短关系链等。
1.深度优先遍历DFS
设计一个程序,能够对给定的迷宫进行路径搜索,并输出一条从起点到终点的路径。具体来说,程序需要实现以下功能:
- 接受用户输入的迷宫地图,包括迷宫的行数和列数,以及每个格子的状态(0 表示可通行,1 表示不可通行)。
- 使用深度优先搜索算法(DFS)对迷宫进行搜索,找到从起点到终点的一条路径。
- 输出搜索到的路径。
入栈以后,然后我们查看栈顶元素,是(0,0)这个节点。栈现在不为空,取栈顶元素,先看它右边能不能走,能走的话,就一直向右走,它右边是0,就入栈了。
_pMaze[0][0]._val == 1
return;
_stack. Push(_pMaze[0][0]);//左上角节点入栈
while (!_stack. Empty())//栈不为空
Node top = _stack. Top();
如果栈顶元素的右边可以走的话,我们要把当前节点的右方向改成不能走,把右边节点的左方向改成不能走。因为不能走回头路,而且因为路子走不通回退后也不能继续走相同的死路。
//往右方向寻找
if (_pMaze[x][y]._state[RIGHT] == YES)
{
_pMaze[x][y]._state[RIGHT] = NO;
_pMaze[x][y + 1]._state[LEFT] = NO;
_stack.push(_pMaze[x][y + 1]);
continue;
深度遍历入栈后,不用判断之前节点的方向。continue后,重新取栈顶元素,继续进行判断。同理,如果右边不能走,下边可以走的话:
//往下方向寻找
if (_pMaze[x][y]._state[DOWN] == YES)
{
_pMaze[x][y]._state[DOWN] = NO;
_pMaze[x + 1][y]._state[UP] = NO;
_stack.push(_pMaze[x + 1][y]);
continue;
}
同理:左方向
//往左方向寻找
if (_pMaze[x][y]._state[LEFT] == YES)
{
_pMaze[x][y]._state[LEFT] = NO;
_pMaze[x][y - 1]._state[RIGHT] = NO;
_stack.push(_pMaze[x][y - 1]);
continue;
}
如果栈顶元素判断完四个方向都不能走,就是到死路了,就把栈顶元素出栈。
然后再取栈顶元素,进行判断,如果它的4个方向都不能走,就出栈,如果栈为空,则迷宫无通路。如果有方向能走,就继续走下去。以此类推下去。但是,都要判断一下此节点是不是右下角的节点,如果是,就是找到通路了。
//已经找到右下角出口得迷宫路径
if (x == _row - 1 && y == _col - 1)
{
return;
}
完整代码:
#include <iostream>
#include <stack>
using namespace std;
//定义迷宫每一个节点的四个方向
const int RIGHT = 0;//右
const int DOWN = 1;//下
const int LEFT = 2;//左
const int UP = 3;//上
//迷宫每一个节点方向的数量
const int WAY_NUM = 4;
//定义节点行走状态
const int YES = 4;//当前方向可以走
const int NO = 5;//当前方向不能走
//迷宫
class Maze
{
public:
//初始化迷宫,根据用户输入的行列数,生成存储迷宫路径信息的二维数组
Maze(int row, int col)
:_row(row)
, _col(col)
{
_pMaze = new Node*[_row];//注意元素类型是Node
for (int i = 0; i < _row; ++i)
{
_pMaze[i] = new Node[_col];
}
}
//初始化迷宫路径节点信息
void initNode(int x, int y, int val)
{
_pMaze[x][y]._x = x;
_pMaze[x][y]._y = y;
_pMaze[x][y]._val = val;
//节点四个方向默认的初始化都为不能走
for (int i = 0; i < WAY_NUM; ++i)
{
_pMaze[x][y]._state[i] = NO;
}
}
//初始化迷宫0节点四个方向的行走状态信息 当前节点右下左上,如果是0,改成可以走
void setNodeState()
{
for (int i = 0; i < _row; ++i)
{
for (int j = 0; j < _col; ++j)
{
if (_pMaze[i][j]._val == 1)
{
continue;//不用调整了,因为走不到值为1的节点
}
//j不能取到最后一列,不然就判断越界了
if (j < _col - 1 && _pMaze[i][j + 1]._val == 0)
//逻辑&,是先计算左边的表达式,左边如果是false,右边就不用计算了
{
_pMaze[i][j]._state[RIGHT] = YES;
}
if (i < _row - 1 && _pMaze[i + 1][j]._val == 0)
{
_pMaze[i][j]._state[DOWN] = YES;
}
//j不用取第一类,因为本身就不能走
if (j > 0 && _pMaze[i][j - 1]._val == 0)
{
_pMaze[i][j]._state[LEFT] = YES;
}
if (i > 0 && _pMaze[i - 1][j]._val == 0)
{
_pMaze[i][j]._state[UP] = YES;
}
}
}
}
//深度搜索迷宫路径
void searchMazePath()
{
if (_pMaze[0][0]._val == 1)
{
return;
}
_stack.push(_pMaze[0][0]);//左上角节点入栈
while (!_stack.empty())//栈不为空
{
Node top = _stack.top();
int x = top._x;
int y = top._y;
//已经找到右下角出口得迷宫路径
if (x == _row - 1 && y == _col - 1)
{
return;
}
//往右方向寻找
if (_pMaze[x][y]._state[RIGHT] == YES)
{
_pMaze[x][y]._state[RIGHT] = NO;
_pMaze[x][y + 1]._state[LEFT] = NO;
_stack.push(_pMaze[x][y + 1]);
continue;
}
//往下方向寻找
if (_pMaze[x][y]._state[DOWN] == YES)
{
_pMaze[x][y]._state[DOWN] = NO;
_pMaze[x + 1][y]._state[UP] = NO;
_stack.push(_pMaze[x + 1][y]);
continue;
}
//往左方向寻找
if (_pMaze[x][y]._state[LEFT] == YES)
{
_pMaze[x][y]._state[LEFT] = NO;
_pMaze[x][y - 1]._state[RIGHT] = NO;
_stack.push(_pMaze[x][y - 1]);
continue;
}
//往上方向寻找
if (_pMaze[x][y]._state[UP] == YES)
{
_pMaze[x][y]._state[UP] = NO;
_pMaze[x - 1][y]._state[DOWN] = NO;
_stack.push(_pMaze[x - 1][y]);
continue;
}
_stack.pop();
}
}
//打印迷宫路径搜索结果
void showMazePath()
{
if (_stack.empty())
{
cout << "不存在一条迷宫路径!" << endl;
}
else
{
while (!_stack.empty())//栈不为空,取出节点坐标,相应值调整为*
{
Node top = _stack.top();
_pMaze[top._x][top._y]._val = '*';
_stack.pop();
}
for (int i = 0; i < _row; ++i)//打印迷宫
{
for (int j = 0; j < _col; ++j)
{
if (_pMaze[i][j]._val == '*')
{
cout << "* ";
}
else
{
cout << _pMaze[i][j]._val << " ";
}
}
cout << endl;
}
}
}
private:
//定义迷宫节点路径信息
struct Node
{
int _x;//节点的横坐标
int _y;//节点的纵坐标
int _val;//节点的值
int _state[WAY_NUM];//记录节点四个方向的状态(左右上下)
//4个元素位置(左右上下),存储yes或者no
};
Node **_pMaze;//动态生成迷宫路径(动态开辟二维数组)
int _row;//迷宫的行
int _col;//迷宫的列
stack<Node> _stack;//栈结构,辅助深度搜索迷宫路径
};
int main()
{
cout << "请输入迷宫的行列数(例如:10 10):";
int row, col, data;
cin >> row >> col;
Maze maze(row, col);//创建迷宫对象
cout << "请输入迷宫的路径信息(0表示可以走,1表示不能走):" << endl;
for (int i = 0; i < row; ++i)//只能获取i,j和data值,节点的4个方向的行走状态还不能初始化
{
for (int j = 0; j < col; ++j)
{
cin >> data;
//可以初始化迷宫节点的基本信息
maze.initNode(i, j, data);
}
}
//开始设置所有节点的四个方向的状态
maze.setNodeState();
//开始从左上角搜索迷宫的路径信息了
maze.searchMazePath();
//打印迷宫路径搜索的结果
maze.showMazePath();
return 0;
}
2.广度优先遍历BFS
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
//定义方向
const int RIGHT = 0;
const int DOWN = 1;
const int LEFT = 2;
const int UP = 3;
const int WAY_NUM = 4;
//定义行走状态
const int YES = 4;
const int NO = 5;
//迷宫
class Maze
{
public:
Maze(int row, int col)
:_row(row)
, _col(col)
{
_pMaze = new Node*[_row];//开辟迷宫二维数组
for (int i = 0; i < _row; ++i)
{
_pMaze[i] = new Node[_col];
}
//node._x*_row + node._y
_pPath.resize(_row * _col);//辅助数组开辟空间
}
void initNode(int x, int y, int val)//初始化为No
{
_pMaze[x][y]._x = x;
_pMaze[x][y]._y = y;
_pMaze[x][y]._val = val;
for (int i = 0; i < WAY_NUM; ++i)
{
_pMaze[x][y]._state[i] = NO;
}
}
void setNodeState()//设置迷宫行走状态
{
for (int i = 0; i < _row; ++i)
{
for (int j = 0; j < _col; ++j)
{
if (_pMaze[i][j]._val == 1)
{
continue;
}
if (j < _col - 1 && _pMaze[i][j + 1]._val == 0)
{
_pMaze[i][j]._state[RIGHT] = YES;
}
if (i < _row - 1 && _pMaze[i + 1][j]._val == 0)
{
_pMaze[i][j]._state[DOWN] = YES;
}
if (j > 0 && _pMaze[i][j - 1]._val == 0)
{
_pMaze[i][j]._state[LEFT] = YES;
}
if (i > 0 && _pMaze[i - 1][j]._val == 0)
{
_pMaze[i][j]._state[UP] = YES;
}
}
}
}
void searchMazePath()//广度优先遍历搜索
{
if (_pMaze[0][0]._val == 1)
{
return;
}
_queue.push(_pMaze[0][0]);//入口节点入队
while (!_queue.empty())//队列不为空
{
Node front = _queue.front();//获取队头元素
int x = front._x;
int y = front._y;
//右方向
if (_pMaze[x][y]._state[RIGHT] == YES)
{
_pMaze[x][y]._state[RIGHT] = NO;
_pMaze[x][y + 1]._state[LEFT] = NO;
//在辅助数组中记录一下节点的行走信息
_pPath[x*_row + y + 1] = _pMaze[x][y];
_queue.push(_pMaze[x][y + 1]);
if (check(_pMaze[x][y + 1]))
return;
}
//下方向
if (_pMaze[x][y]._state[DOWN] == YES)
{
_pMaze[x][y]._state[DOWN] = NO;
_pMaze[x + 1][y]._state[UP] = NO;
_pPath[(x + 1)*_row + y] = _pMaze[x][y];
_queue.push(_pMaze[x + 1][y]);
if (check(_pMaze[x + 1][y]))
return;
}
//左方向
if (_pMaze[x][y]._state[LEFT] == YES)
{
_pMaze[x][y]._state[LEFT] = NO;
_pMaze[x][y - 1]._state[RIGHT] = NO;
_pPath[x*_row + y - 1] = _pMaze[x][y];
_queue.push(_pMaze[x][y - 1]);
if (check(_pMaze[x][y - 1]))
return;
}
//上方向
if (_pMaze[x][y]._state[UP] == YES)
{
_pMaze[x][y]._state[UP] = NO;
_pMaze[x - 1][y]._state[DOWN] = NO;
_pPath[(x - 1)*_row + y] = _pMaze[x][y];
_queue.push(_pMaze[x - 1][y]);
if (check(_pMaze[x - 1][y]))
return;
}
//当前节点出队列
_queue.pop();
}
}
void showMazePath()//打印迷宫
{
if (_queue.empty())
{
cout << "不存在一条迷宫路径!" << endl;
}
else
{
//回溯寻找迷宫路径节点
int x = _row - 1;
int y = _col - 1;
for (;;)
{
_pMaze[x][y]._val = '*';
if (x == 0 && y == 0)
break;
Node node = _pPath[x*_row + y];
x = node._x;
y = node._y;
}
for (int i = 0; i < _row; ++i)
{
for (int j = 0; j < _col; ++j)
{
if (_pMaze[i][j]._val == '*')
{
cout << "* ";
}
else
{
cout << _pMaze[i][j]._val << " ";
}
}
cout << endl;
}
}
}
private:
//定义迷宫节点路径信息
struct Node
{
int _x;
int _y;
int _val;//节点的值
int _state[WAY_NUM];//记录节点四个方向的状态
};
//检查是否是右下角的迷宫出口节点
bool check(Node &node)
{
return node._x == _row - 1 && node._y == _col - 1;
}
Node **_pMaze;//动态开辟二维数组
int _row;//行
int _col;//列
queue<Node> _queue;//广度遍历依赖的队列结构
vector<Node> _pPath;//记录广度优先遍历时,节点的行走信息,辅助数组
};
int main()
{
cout << "请输入迷宫的行列数(例如:10 10):";
int row, col, data;
cin >> row >> col;
Maze maze(row, col);//创建迷宫对象
cout << "请输入迷宫的路径信息(0表示可以走,1表示不能走):" << endl;
for (int i = 0; i < row; ++i)
{
for (int j = 0; j < col; ++j)
{
cin >> data;
//可以初始化迷宫节点的基本信息
maze.initNode(i, j, data);
}
}
//开始设置所有节点的四个方向的状态
maze.setNodeState();
//开始从左上角搜索迷宫的路径信息了
maze.searchMazePath();
//打印迷宫路径搜索的结果
maze.showMazePath();
return 0;
}