什么是DFS
深度优先搜索属于算法的一种,其过程是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。
简单的来说就是:一路走到头,不撞南墙不回头。
案例图的遍历
遍历上图,图中部分节点之间有路径连接。遍历规则为:深度优先,右手路径优先,不能重复遍历。那么遍历的结果是什么?
ABCDEFGHI
例题 扫地机器人
思路
典型的DFS的题。其实优先搜索可以通过if语句来实现(注意不能使用if-else语句 但是此题可以使用if-else语句,因为DFS要全部路径都包括,会退回一些点尝试没有尝试过的路)。既然优先向右来搜索,那么就让y一直+1,直到等于m然后再向下搜索,注意向下搜索在此题中只会走一次。再是向左搜索,依次思路写即可。
AC代码
代码1
#include <iostream>
using namespace std;
//为2维数组赋值 为xy点赋值为k
int n, m;//定义全局变量 使两个函数都可以访问到m与n
int a[1000][1000];
void f(int x, int y, int k) {
a[x][y] = k; //赋值为k
//优先向右 其次向下 再向左 最后是下
//递归的条件不能出边界不能访问已经赋值的点
//向右
if (y + 1 <= m && a[x][y + 1] == 0)
f(x, y + 1, k + 1);
//向下 仅有一次
else if (x + 1 <= n && a[x+1][y] == 0)
f(x + 1, y, k + 1);
//向左
else if (y - 1 >= 1 && a[x][y - 1] == 0)
f(x, y - 1, k + 1);
//向上
else if (x - 1 >= 1 && a[x - 1][y] == 0)
f(x - 1, y, k + 1);
}
int main() {
cin >> n >> m;
f(1, 1, 1);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cout << setw(3) << a[i][j];
}
cout << endl;
}
}
代码2
#include <iostream>
using namespace std;
int n, m;
int a[1000][1000];
void f(int x, int y, int k) {
//递归的条件不能出边界并且不能访问已经赋值的点
if (x >= 1 && x <= n && y >= 1 && y <= m && a[x][y] == 0) {
a[x][y] = k; //赋值为k
//优先向右 其次向下 再向左 最后是下
//向右
f(x, y + 1, k + 1);//step1
//向下
f(x + 1, y, k + 1);//step2
//向左
f(x, y - 1, k + 1);//step3
//向上
f(x - 1, y, k + 1);//step4
}
}
int main() {
cin >> n >> m;
f(1, 1, 1);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cout << setw(3) << a[i][j];
}
cout << endl;
}
}
其实可以不用那么麻烦的判断,直接四个递归的。如上述代码,如果比如对于2*2的网格,点(1,3)不能进入step1,就会进入step2.本质上还是DFS的应用。这样的写法每个step的本质就是if判断,点合法则可以进去否则进不去就会进入下一个step。
代码3
#include <iostream>
using namespace std;
int n, m;
int a[1000][1000];
int fx[5] = {0, 0, 1, 0, -1};
int fy[5] = {0, 1, 0, -1, 0};
void f(int x, int y, int k) {
a[x][y] = k; //赋值为k
//优先向右 其次向下 再向左 最后是下
//递归的条件不能出边界不能访问已经赋值的点
int tx, ty;
for (int i = 1; i < 5; i++) {
tx = x + fx[i];
ty = y + fy[i];
if (tx >= 1 && tx <= n && ty >= 1 && ty <= m && a[tx][ty] == 0)
f(tx, ty, k + 1);
}
}
int main() {
cin >> n >> m;
f(1, 1, 1);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cout << setw(3) << a[i][j];
}
cout << endl;
}
}
思路不变 代码更简洁,效率更高,在递归之前判断就会防止不合法的点进入递归,使效率稍微高那么一点点。