BFS介绍
BFS,其英文全称是Breadth First Search。 BFS并不使用经验法则算法。从算法的观点,所有因为展开节点而得到的子节点都会被加进一个先进先出的队列中。一般的实验里,其邻居节点尚未被检验过的节点会被放置在一个被称为 open 的容器中(例如队列或是链表),而被检验过的节点则被放置在被称为 closed 的容器中。宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
总结来说:广度优先算法的核心是它一定是把同一级的子节点都拉入队列,之后才会去遍历下一级节点
队列的知识
其他资料
BFS模板
void BFS(int s){
queue<int> q;
q.push(s);
while(!q.empty()){
取出队首元素top;
访问队首元素top;
将队首元素出队;
将top的下一层结点未曾入队的结点全部入队,并设置为已入队
}
}
图示
练习
1、迷宫最短路径
迷宫由n行m列的单元格组成(n,m都小于50),每个单元格要么是空地1,要么是障碍物0。请你找出一条从起点到终点的最短路径长度。
输入:
(起点和终点的坐标)startx starty finshx finshy 还有迷宫
输出:
最短路径长度t或者”没有终点“
#include <bits/stdc++.h>
using namespace std;
int map1[100][100];//地图大小
int v[100][100]; //判断是否走过
struct point
{
int x;
int y;
int step;
};
queue<point> r;
int dir[4][4]={
{
0,1},{
1,0},{
-1,0},{
0,-1}}; //四个方向:右下上左
int flag=0;
void bfs(int startx,int starty,int finshx,int finshy)
{
point start;
start.x=startx;
start.y=starty;
start.step=0;
r.push(start); //将起点入队
v[startx][starty]=1;//设置为已经访问
while(!r.empty())
{
int x=r.front().x;
int y=r.front().y;
if(x==finshx && y==finshy)
{
flag=1;
cout << r.front().step;
break;
}
for(int i=0;i<4;i++)
{
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(map1[tx][ty]==1 && v[tx][ty]==0)
{
//入队
point temp;//临时变量
temp.x=tx;
temp.y=ty;
temp.step=r.front().step+1;
r.push(temp);
v[tx][ty]=1;
}
}
r.pop();//拓展完成之后需要将队首元素出队
}
}
int main()
{
int m,n;
cin >> m >> n;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin >> map1[i][j];
}
int startx,starty,finshx,finshy;
cin >>startx>>starty>>finshx>>finshy;
bfs(startx,starty,finshx,finshy);
if(flag==0)
cout << "没有终点" << endl;
return 0;
}
2、迷宫问题变种练习
#include<bits/stdc++.h>
using namespace std;
int vis[50][50];
struct point{
int x;
int y;
int z; //剩余步数
int step; //已经走过的步数
};
int dir[4][2]={
1,0,-1,0,0,1,0,-1};
queue<point> q;
int m,n;
int bfs(int x,int y,int z)
{
int xx,yy,zz;
vis[x][y]=0;
point temp1;
temp1.x=x;
temp1.y=y;
temp1.z=z;
temp1.step=0;
q.push(temp1);
while(!q.empty())
{
temp1=q.front();
q.pop();
for(int i=0;i<4;i++)
{
xx=temp1.x+dir[i][0];
yy=temp1.y+dir[i][1];
zz=temp1.z-1;
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&vis[xx][yy]!=0 && (zz>1 || (vis[xx][yy]!=1 && zz==1)))
{
point temp2;
temp2.x=xx;
temp2.y=yy;
temp2.step=temp1.step+1;
temp2.z=zz;
if(vis[xx][yy]==4)
{
vis[xx][yy]=0;
temp2.z=6;
}
if(vis[xx][yy]==3)
{
return temp2.step;
}
q.push(temp2);
}
}
}
return -1;
}
int main()
{
int sx,sy,o;
cin >>o;
for(int i=1;i<=o;i++)
{
cin >> n >>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>vis[i][j];
if(vis[i][j]==2)
{
sx=i;
sy=j;
}
}
int ans=bfs(sx,sy,6);
cout << ans << endl;
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int maxn=301;
int Map[maxn][maxn];
int dir[8][2]= {
-2,-1,-2,1,-1,2,1,2,2,1,2,-1,1,-2,-1,-2};
int l,sx,sy,lx,ly,o,ans;
struct node
{
int x,y,step;
} now,nextt;
int BFS(int x,int y)
{
queue<node>q;
int xx,yy;
Map[x][y]=1;
now.x=x;
now.y=y;
now.step=0;
q.push(now);
while(!q.empty())
{
now=q.front();
q.pop();
for(int i=0; i<8; i++) //注意八个方向
{
xx=now.x+dir[i][0];
yy=now.y+dir[i][1];
if(xx>=0&&xx<l&&yy>=0&&yy<l&&Map[xx][yy]!=1)
{
nextt.x=xx;
nextt.y=yy;
nextt.step=now.step+1;
if(xx==lx && yy==ly)
return nextt.step;
q.push(nextt);
Map[xx][yy]=1;
}
}
}
}
int main()
{
cin >> o;
while(o--)
{
cin >> l >> sx >> sy >> lx >> ly;
memset(Map,0,sizeof(Map));
if(sx==lx && sy==ly)
ans = 0;
else
ans=BFS(sx,sy);
printf("%d\n",ans);
}
return 0;
}
3、别的应用(连通块)
POJ - 1562
注意方向和BFS运用方法改变
#include <iostream>
#include <queue>
#include <cstdio>
using namespace std;
const int maxn=105;
char Map[maxn][maxn];
int n,m,cnt=0;
int dir[8][2]={
{
1,0},{
1,1},{
1,-1},{
-1,0},{
-1,1},{
-1,-1},{
0,1},{
0,-1}};
struct node
{
int x;
int y;
}now,next;
void BFS(int x,int y)
{
now.x=x;
now.y=y;
Map[x][y]='*';
queue<node> q;
q.push(now);
while(!q.empty())
{
now=q.front();
q.pop();
for(int i=0;i<8;i++)
{
int xx=now.x+dir[i][0];
int yy=now.y+dir[i][1];
if(xx>=0&&xx<n&&yy>=0&&y<m&&Map[xx][yy]=='@')
{
Map[xx][yy]='*';
next.x=xx;
next.y=yy;
q.push(next);
}
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
cnt=0;
if(n==0)///输入0 0 退出
break;
for(int i=0;i<n;i++)
{
scanf("%s",Map[i]);
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(Map[i][j]=='@')
{
cnt++;
BFS(i,j);
}
}
}
printf("%d\n",cnt);
}
return 0;
}
4、最短距离
一位农夫在点n上,他要到奶牛所在的点k上,
他可以每次从点X到点X-1或点X+1或点2*X,问他到达点k的最短次数.
(0 ≤ N ≤ 100,000,0 ≤ K ≤ 100,000)
样例:
Sample Input
5 17
Sample Output
4
#include <bits/stdc++.h>
#define pp pair<int,int>
using namespace std;
int n,k,ans;
queue<pp>q;
bool v[200004];
void bfs() {
q.push(pp(n,0));//先将n丢进队列
v[n]=1;
while(!q.empty()) {
pp now=q.front();
q.pop();
if(now.first==k) {
ans=now.second;
break;
}
now.second++;//接下来我们有3种操作,将现在的位置now.second 加1 或 减1 或 乘2
if(!v[now.first+1]&&now.first<k) {
//边界条件不能少了
q.push(pp(now.first+1,now.second));
v[now.first+1]=1;//将已经走过的点标记为1,为什么呢??q队列中到这个数的次数是从小到大排序的,now.first+1这个点刚通过now.first被拜访过,它的此时次数肯定小于等于下一次拜访的次数.想一想为什么.
}
if(!v[now.first-1]&&now.first-1>=0) {
q.push(pp(now.first-1,now.second));
v[now.first-1]=1;
}
if(now.first<k&&(!v[now.first*2])) {
q.push(pp(now.first*2,now.second));
v[now.first*2]=1;
}
}
return;
}
int main() {
cin >> n >> k;
bfs();
cout << ans << endl;
return 0;
}