BFS模板(邻接矩阵版本)

BFS的大体结构:

 

一、预处理:

1:首先,需要定义一个:用来表示单位结点的struct结构体

    思考一下,为了表示某个点上的状态,我需要记录什么?

     ——首先肯定有位置(思考思考,这里位置使用二维坐标表示,还是用一维坐标表示??)

2:考虑一下,需不需要一个vis数组,来避免重复搜索?

3:定义一个,约束条件检验函数 (is_ok),主要判断:是否走到图边界,是否已经走过了

 

 

二、bfs主函数(伪代码):

void bfs (传入起始结点的位置)

{

        定义队列 q

        配置、压入首节点,并把首节点pop出队列

        访问标记

   while(!q.empty())

   {

        取出队列头结点,作为本次处理的点对象。

        检验:此点是否已经达到目标状态,如果达到,直接结束bfs。(记住:检验在扩展之前)

        如果没达到,开始从这个点发散扩展,并将扩展找到的所有合法结点,压入队列末尾。

   }

   return ;

}

 

 

代码(邻接矩阵):

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=100;


int dir[4][2]={0,1,0,-1,1,0,-1,0}; // 方向向量
bool vis[maxn][maxn]; // 访问标记。我的习惯上,统一放在外面,不放在结构体里面。


struct node // BFS 队列中,每一个单位结点的,状态数据结构。
{
    int x,y;  // 坐标位置
              //这里注意,并不是每道题的单位结点,都用x和y两个数据来标记位置。
              //有些题,一维就足以标记自己的位置了  (比如,专题6  1004)
int dpth; // 记录该节点的深度 }; //相当于是,把结点打包为一个整体的结构体,压入队列。 //bfs是以队列里的每个元素为单位,记录自己的dpth
//实际上,用二维坐标表示点的位置,常见于:题目给的输入,天然就是一张图,自带双维点坐标(比如那种“迷宫格”一样的题,专题6绝大部分题)
//而用一维表示图中点的位置(也就是说,只用标号,表示某个点),常见于:题目给的输入,并不是图,图是自己构建的(比如专题6的1004)
node a[maxn];

bool is_ok(node s) // 约束条件检验
{
    if(!vis[s.x][s.y] && ...)
        return 1;
    else
        return 0;
}

void bfs(int x,int y)//传入:本次bfs搜索的,起始点。(从一个点开始,向外扩展)
{
    queue <node> q; // BFS 队列
    
    node sta;   //配置起始点
    sta.dpth=0;
    sta.x=x;
    sta.y=y;
    q.push(sta); // 把起始点入队
    vis[sta.x][sta.y]=1; // 访问标记
    
    while(!q.empty())
    {
        node pro=q.front(); // 取队首元素为本次待处理元素,作为后续扩展的起点
        q.pop();
        

        if(pro==G)   //首先判断:本次待处理元素(现在站的这个位置),是否已经满足“目标状态”
                     //请注意,在此节点被扩展之前,就进行这个判断。
        {
            ......
            
            return;
        }
        //找到答案,直接输出,结束程序。因为:
        //是广度优先搜索,越往后step越大,所以最先找到的step就是最小的那个。
        
        
        
        for(int i=0;i<4;i++)      //扩展,遍历地寻找每一个相邻点。
        {
            node tmp;             //生成下一个状态
            tmp.x=pro.x+dir[i][0];
            tmp.y=pro.y+dir[i][1];
            tmp.dpth=pro.dpth+1;
            
            if(is_ok(tmp))    // 如果状态满足约束条件,则入队
            {
                q.push(tmp);
                vis[tmp.x][tmp.y]=1;   //访问标记,标记为已经走过,走过的点不再重复走。
                                       //请记住:每次,只要你把某个节点加入队列了,都要标记该节点的vis。
            }
        }
        
    }
    return;
}

int main()
{
    ......   //主函数里面,主要搞清楚,是只从一个节点开始就可以了,还是要循环地从每个结点,遍历开始。
             //也就是说,是一个bfs就够了,还是要多个bfs。
    return 0;
}

 

再说说邻接表:

至于邻接表的bfs,其实和邻接矩阵基本一样,要点就在于:

邻接表如何存储图,又如何调出图数据

在bfs里,也就是说:给定一个点,在邻接表中,如何找到它的所有邻接点??

 

这再简单不过了。

1:邻接图的存储,可以用vector数组,也可以用set数组,用set可以避免重复存储相同结点,二者酌情选用。

存储的都是:与这个点相连的,所有节点,的编号(实质上,存储的是,能够完整表示某个节点的信息。通过这里面信息,要能找出所需点才行。)

(例如: vector<int>  mp[20]  , 或者set<int>  mp[20})

          (或者map<node> mp[maxn]————当节点信息不能一维表示的时候,可用map,从某个node映射到与他相良的所有node)

2:找到某个点x的所有邻接点,只需要遍历容器mp[x]即可。

顺便补充,set的遍历:

   for(auto i:mp[x])

   {

      if(is_correct(i))

      {

          q.push(i);

          vis[i]=1;

      }

   }

 

简而言之,邻接表的bfs和邻接矩阵基本一样,只需要在:主函数(读入图的时候) 和  当前pro结点扩张的时候(遍历某个点的邻接点的时候)  变一下,即可。

另外,只在每个点用一维(也就是一个参数,比如点标号)表示的时候,邻接表才好用。

如果是那种“迷宫格”的题,邻接表似乎就不太好用。

 

(邻接表bfs做题链接:专题6  1004  邻接表版本)

posted on 2018-04-22 15:51  _isolated  阅读(495)  评论(0)    收藏  举报