看到一道题,题解总结了bfs和dfs的模板,来记录下。
一:bfs
bfs是按一层一层来访问的,适合有目标求最短路的步数,你想想层层搜索每次层就代表了一步。bfs优先访问的是兄弟节点(相邻的节点),只有这一层全部访问完才能访问下一层,也就是说bfs第几层就代表当前可以走到的位置(结点).
二:dfs
dfs是按递归来实现的,它优先搜索深度,再回溯。优先访问的是没有访问过的子节点。DFS多用于连通性问题因为其运行思想与人脑的思维很相似,故解决连通性问题更自然。
以下是bfs和dfs的简单框架
一:bfs
模板:
第一种://bfs模板
struct ed
{
.....
}
deque<ed>q;//当然用queue也可以
void bfs()
{
标记起点
起点入队列
while(!q.empty())//队列不为空
{
ed nw=q.front();//返回队首
for(拓展出接下来可能的状态)
{
ed nxt;
记录这一状态
判断状态是否合法
标记状态
q.push_back(nxt);//状态入队列
}
q.pop_front();//弹出队首
}
}
第二种:
/**
* 广度优先搜索
* @param Vs 起点
* @param Vd 终点
*/
bool BFS(Node& Vs, Node& Vd){
queue<Node> Q;
Node Vn, Vw;
int i;
//初始状态将起点放进队列Q
Q.push(Vs);
hash(Vw) = true;//设置节点已经访问过了!
while (!Q.empty()){//队列不为空,继续搜索!
//取出队列的头Vn
Vn = Q.front();
//从队列中移除
Q.pop();
while(Vw = Vn通过某规则能够到达的节点){
if (Vw == Vd){//找到终点了!
//把路径记录,这里没给出解法
return true;//返回
}
if (isValid(Vw) && !visit[Vw]){
//Vw是一个合法的节点并且为白色节点
Q.push(Vw);//加入队列Q
hash(Vw) = true;//设置节点颜色
}
}
}
return false;//无解
}
二:dfs
模板:
第一种://也是一次走到底,然后回溯
void dfs()
{
for(拓展状态)
{
判断合法
记录
dfs(继续搜);
回溯;
}
}
*/
第二种:
void dfs(){
搜索上下左右四个位置是否符合条件。
if(符合条件) {
标记
dfs()
}
}
根据模板,记录一道题,方便理解
题目:
一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。
输入格式
第一行两个整数代表矩阵大小 n 和 m。
接下来 n 行,每行一个长度为 m 的只含字符 0
到 9
的字符串,代表这个 n \times m n×m 的矩阵。
输出格式
一行一个整数代表细胞个数。
输入输出样例
输入 #1
4 10
0234500067
1034560500
2045600671
0000000089
输出 #1
4
其实思路很简单就是连通块,为了便于理解,我特地将数字0标红,结果发现就是只有四个被数字0包裹住的连通块。
首先是bfs的解法:
#include<iostream>
#include<deque>//双向队列头文件
using namespace std;
struct pp
{
int x, y;
};//初始化函数
deque<pp> q;//队列
int n, m, ans = 0;//n行m列,ans为答案
int a[105][105];//存矩阵
bool used[105][105];//记录是否走过
int dx[4] = { -1,1,0,0 };//向上下左右走一步行号和列好的改变
int dy[4] = { 0,0,-1,1 };
void bfs(int sx, int sy)//bfs
{
pp st;
st.x = sx; st.y = sy;
used[sx][sy] = 1;
q.push_back(st);
while (!q.empty())
{
pp nw = q.front();
for (int i = 0; i < 4; i++)
{
pp nxt = nw;
nxt.x += dx[i];
nxt.y += dy[i];
if (a[nxt.x][nxt.y] == 0 || used[nxt.x][nxt.y] == 1) continue;//为零的就不用在管他了
used[nxt.x][nxt.y] = 1;//把这一连通块的点染色
q.push_back(nxt);
}
q.pop_front();//因为是双向队列。这可以让遍历过的数出去
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%1d", &a[i][j]);//遍历地图
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (used[i][j] == 0 && a[i][j] != 0)
{
bfs(i, j);
ans++;//若这一连通块没搜过ans++
}
}
}
cout << ans;
return 0;
}
其次时dfs的方法,因为都是连通块,相差不太大
#include<iostream>
using namespace std;
int n, m, ans = 0;
int a[105][105];
bool used[105][105];
int dx[4] = { -1,1,0,0 };
int dy[4] = { 0,0,-1,1 };
void dfs(int x, int y)
{
used[x][y] = 1;
for (int i = 0; i < 4; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
if (a[nx][ny] == 0 || used[nx][ny] == 1) continue;
dfs(nx, ny);//回溯
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%1d", &a[i][j]);//遍历地图
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (used[i][j] == 0 && a[i][j] != 0)
{
dfs(i, j);
ans++;//寻找连通块
}
}
}
cout << ans;
return 0;
}
总之
一个着重队列,一个着重是回溯,嗯,就这样。