基本搜索算法(DFS|BFS)
这周的题目中设计到的算法:DFS
和 BFS
都是基本的图算法,图是一种数据结构,可以表示出节点之间的关系。
基本搜索算法有两种策略:
- 深度优先
- 广度优先
[1] 深度优先搜索
我们对一个图进行搜索,无非就是寻找某种状态,深度优先顾名思义,就是寻找某种状态的时候选择一条路走到底,走不通就退回去换另一条路。
就像走迷宫那样,我们把迷宫抽象为一个图,路就是图里面的边,路两端的地方抽象为节点。走出这个迷宫就可以看作是我们要寻找的一个状态,在不清楚迷宫构造和不破换游戏规则的情况下,找到出口只能一个一个去尝试,为了不出现在同一条路上绕来绕去的尴尬情况,我们需要标记过我们到过的地方,然后一直往深处走,走到头若发现此路不通或已经走过,那自然就要原路返回到前一个岔路口去走另一条路。这种方法虽然比较笨比较低效,但只要迷宫有出口就一定可以出的去。
有了这个例子,很容易归纳出深度优先的基本方法:
- 选择一个初始节点
- 从这个初始节点开始寻找,标记走过的节点
- 如果走到不能再走,回到前一个可以走另一条路的状态(回溯)
- 找到目标状态、退出
因为我们很难判断要走多少层,再由第3步很容易联想到递归。
那么找到目标状态和走到不能再走就是递归的结束条件。
DFS 理解起来到是不难,关键在于对实际问题的抽象,下面是几道关于DFS的题
- 5×5迷宫
定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
Input
一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。
Output
左上角到右下角的最短路径,格式如样例所示。
这道题和之前举得例子相似,只是多了两个条件:1.路要最短 2.要记录走过的路径
那就需要走过所有的路,记录每条路所走过的路径。
代码如下:
#include <stdio.h>
#include <string.h>
#define MAXN 5
int vst[MAXN][MAXN]; // 访问标记,记录是否走过
int map[MAXN][MAXN]; // 坐标范围
int depth = 0; // 路长
int mindh = MAXN * MAXN;
int path[MAXN * MAXN][MAXN * MAXN];
int min, count = 0;
int dir[4][2] =
{
{
0, 1}, {
0, -1}, {
1, 0}, {-1, 0}
};
int CheckEdge(int x, int y) // 边界、约束判断
{
// 如果节点没走过且没有越界
if ((x >= 0 && x < MAXN && y >= 0 && y < MAXN) && !map[x][y] && !vst[x][y])
return 1;
return 0;
}
void dfs(int x, int y)
{
path[count][depth] = x*10 + y;
depth++;
vst[x][y] = 1; // 标记访问过的节点
if (x == MAXN - 1 && y == MAXN - 1)
{
if (depth < mindh)
{
mindh = depth; // 更新最短路径
min = count; // 记录存储最短路径节点的下标
count++;
memcpy(path[count], path[count-1], sizeof(path[count-1]));
}
return;
}
// 往其他的方向尝试
for (int i = 0; i < 4; i++)
{
if (CheckEdge(x + dir[i][0], y + dir[i][1]))
{
dfs(x + dir[i][0], y + dir[i][1]);
vst[x + dir[i][0]][y + dir[i][1]] = 0;
depth--; // 走另一条路
}
}
return;
}
<