BSF深度优先遍历的练习

广度优先遍历类似于树中的层序遍历


以迷宫探索为例,假设入口时A(第一层),与A直接相连的岔口有B,C(第二层),然后开始探索B,和B直 接 相连的岔路口有D,E(第三次),与C直接相连的岔路口有F,G(第三层),探索完B后探索C,之后又开始探索E, F,G,H,…

对于广度优先遍历可以用队列实现

1.现在队列里放入起始点A,然后取队首元素A,将与A直接相连的岔口B,C入队,此时队里有{B,C}
2.队首元素B出队,和B直接相连的岔路口D,E入队对列里此时为{C,D,E},
3.队首元素出队,和B直接相连的岔路口F,G入队对列里此时为{D,E,F,G},
4.以此类推,逐层遍历。
基本模板如下:


 void BFS(int s){
  queue<int> q;
  q.push(s);
  while(!q.empty()){
   取队首元素;
   访问(输出)队首元素;
   将队首元素出队;
   将队首元素的下一层的节点中未曾入队的节点全部入队;
  }
 }

给定一个m*n的矩阵,矩阵中的元素为0或1,定义位置(x,y)的上,下,左,右,四个位置(x,y+1),(x,y-1),(x+1,y),(x-1,y),四个位置是相邻的,如果矩阵中有些1是相邻的,称他们构成了一个块,求给定矩阵中的快的个数
0111001
0010000
0000100
0001110
1110100
1111000
这个6*7矩阵的块数为4

 #include<iostream>
 #include<queue>
 using namespace std;
 const int maxn=100;      
 struct node{                 
 int x,y;
 }Node;
 int n,m;
 int store[maxn][maxn];              //存储每个位置的元素
 bool inq[maxn][maxn]={false};       //标记元素是否入队过
 int X[]={0,0,1,-1};               
 int Y[]={1,-1,0,0};                 //定义四个位置
 bool judge(int x,int y){             //判断该节点的元素是否需要访问
  if(x>n||x<0||y>m||y<0)
   return false;
  if(inq[x][y]==true||store[x][y]==0)
   return false;
  return true;
 }
 void BFS(int x,int y){                  
  queue<node> Q;
  Node.x=x,Node.y=y;
  Q.push(Node);
  inq[x][y]=true;                  //标记该节点已入队
  while(!Q.empty()){
   node top=Q.front();    //取出该节点
   Q.pop();              //弹出队首元素
   for(int i=0;i<4;i++){    //遍历相邻四个元素,判断是否需要访问,
    int newx=top.x+X[i];
    int newy=top.y+Y[i];
    if(judge(newx,newy)){
     Node.x=newx;
     Node.y=newy;
     Q.push(Node);
     inq[newx][newy]=true;   //对入队的元素标记
    }
   }
  }
 }
 int main(){
  cin>>n>>m;
  for(int i=0;i<n;i++)
   for(int j=0;j<n;j++)
    cin>>store[i][j];
  int sum=0;
  for(int i=0;i<n;i++)
   for(int j=0;j<m;j++){
    if(store[i][j]==1&&inq[i][j]==false){ //如果元素为1,且未被访问过
     sum++;
     BFS(i,j);
    }
        } 
  cout<<"sum is :"<<sum<<endl;
 return 0;
 }

一个类似的例子

给定一个nm的迷宫,其中“”代表不可穿越的墙壁,而“.”代表平地,S代表起点,T代表终点,从起点开始移动,只能向上下左右四个方向移动,求从起点到终点的最少步数。。。
。 。。 。 。
。 * 。 * 。
。 * S * 。
。 。 。 T *
S的坐标是[2,2],T的坐标是[3,4]

 #include<iostream>
 #include<queue>
 using namespace std;
 const int maxn=100;
 struct node{
  int x,y;
  int step;
 }S,T,Node;
 int n,m;
 char store[maxn][maxn];
 bool inq[maxn][maxn]={false};
 int X[]={0,0,1,-1};
 int Y[]={1,-1,0,0};
 bool judge(int x,int y){
  if(x>n||x<0||y>m||y<0)
   return false;
  if(inq[x][y]==true||store[x][y]=='*')
   return false;
  return true;
 }
 int BFS(){
  queue<node> Q;
  Q.push(S);
  inq[S.x][S.y]=true;
  while(!Q.empty()){
   node top=Q.front();
   Q.pop();
   if(top.x==T.x&&top.y==T.y)
    return top.step;            //找到出口
   for(int i=0;i<4;i++){
    int newx=top.x+X[i];
    int newy=top.y+Y[i];
    if(judge(newx,newy)){
     Node.x=newx;
     Node.y=newy;
     Node.step=top.step+1;
     Q.push(Node);
     inq[newx][newy]=true;
    }
   }
  }
  return -1;   //未找到出口
 } 
 int main(){
  cin>>n>>m;
  for(int i=0;i<n;i++)
   for(int j=0;j<n;j++)
   cin>>store[i][j];
  cout<<"start x,y and end x,y"<<endl;
  cin>>S.x>>S.y>>T.x>>T.y;
  cout<<"sum is :"<<BFS()<<endl;
  return 0;
 }

注意当使用STL的queue时,元素入队的push()操作,只是制造了该元素的副本入队,因此,在队列里对该该元素的修改不会影响到原来的元素,对原来元素的修改也不会影响到副本元素。如下面的例子:


 #include<iostream>
 #include<queue>
 using namespace std;
 struct node{
  int data;
 }a[10];
 int main()
 {
  queue <node> Q;
  for(int i=0;i<10;i++){
   a[i].data=i;
   Q.push(a[i]);
  }
  Q.front().data=100;
  cout<<a[0].data<<" "<<Q.front().data<<" ";
  Q.pop();
  a[1].data=20;
  cout<<a[1].data<<" "<<Q.front().data;
  return 0;
 }       //结果时0 100 20 1

如果在元素在队列里时要修改元素,而不是访问它,此时可以把他的序号入队,而不是元素本身,修改上面的代码如下:


 #include<iostream>
 #include<queue>
 using namespace std;
 struct node{
  int data;
 }a[10];
 int main()
 {
  queue <int> Q;
  for(int i=0;i<10;i++){
   a[i].data=i;
   Q.push(i);
  }
  a[Q.front()].data=100;
  cout<<a[0].data<<" "<<a[Q.front()].data<<" ";
  Q.pop();
  a[1].data=20;
  cout<<a[1].data<<" "<<a[Q.front()].data;
  return 0;
 }   //结果为100 100 20 20

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值