深搜(DFS)广搜(BFS)详解

图的深搜与广搜

一、介绍:

BFS:这是一种基于队列这种数据结构的搜索方式,它的特点是由每一个状态可以扩展出许多状态,然后再以此扩展,直到找到目标状态或者队列中头尾指针相遇,即队列中所有状态都已处理完毕。

DFS:基于递归的搜索方式,它的特点是由一个状态拓展一个状态,然后不停拓展,直到找到目标或者无法继续拓展结束一个状态的递归。

 

优缺点:

BFS:对于解决最短或最少问题特别有效,而且寻找深度小,但缺点是内存耗费量大(需要开大量的数组单元用来存储状态)。

DFS:对于解决遍历和求所有问题有效,对于问题搜索深度小的时候处理速度迅速,然而在深度很大的情况下效率不高

 

总结:不管是BFS还是DFS,它们虽然好用,但由于时间和空间的局限性,以至于它们只能解决数据量小的问题。

二、过程

1.广度优先搜索(BFS


广度优先搜索在进一步遍历图中顶点之前,先访问当前顶点的所有邻接结点。


  a .
首先选择一个顶点作为起始结点,并将其染成灰色,其余结点为白色。
  b.
将起始结点放入队列中。
  c.
从队列首部选出一个顶点,并找出所有与之邻接的结点,将找到的邻接结点放入队列尾部,将已访问过结点涂成黑色,没访问过的结点是白色。如果顶点的颜色是灰色,表示已经发现并且放入了队列,如果顶点的颜色是白色,表示还没有发现
  d.
按照同样的方法处理队列中的下一个结点。
基本就是出队的顶点变成黑色,在队列里的是灰色,还没入队的是白色。

 


用一副图来表达这个流程如下:

1.初始状态,从顶点1开始,队列={1}

2.访问1的邻接顶点,1出队变黑,2,3入队,队列={2,3,}

3.访问2的邻接结点,2出队,4入队,队列={3,4}

4.访问3的邻接结点,3出队,队列={4}

5.访问4的邻接结点,4出队,队列={ }


从顶点1开始进行广度优先搜索:

初始状态,从顶点1开始,队列={1}

访问1的邻接顶点,1出队变黑,2,3入队,队列={2,3,}

访问2的邻接结点,2出队,4入队,队列={3,4}

访问3的邻接结点,3出队,队列={4}

访问4的邻接结点,4出队,队列={ }


结点5对于1来说不可达。

 

 

2.深度优先搜索(DFS

深度优先搜索在搜索过程中访问某个顶点后,需要递归地访问此顶点的所有未访问过的相邻顶点。

初始条件下所有节点为白色,选择一个作为起始顶点,按照如下步骤遍历:

 

a. 选择起始顶点涂成灰色,表示还未访问

b. 从该顶点的邻接顶点中选择一个,继续这个过程(即再寻找邻接结点的邻接结点),一直深入下去,直到一个顶点没有邻接结点了,涂黑它,表示访问过了

c. 回溯到这个涂黑顶点的上一层顶点,再找这个上一层顶点的其余邻接结点,继续如上操作,如果所有邻接结点往下都访问过了,就把自己涂黑,再回溯到更上一层。

d. 上一层继续做如上操作,知道所有顶点都访问过。


用图可以更清楚的表达这个过程:(注意:图画错了,请将3->4这条路径理解成4->3)

1.初始状态,从顶点1开始

2.依次访问过顶点1,2,3后,终止于顶点3(注意,是4->3)

3.从顶点3回溯到顶点2,继续访问顶点5,并且终止于顶点5

4.从顶点5回溯到顶点2,并且终止于顶点2

5.从顶点2回溯到顶点1,并终止于顶点1

6.从顶点4开始访问,并终止于顶点4


从顶点1开始做深度搜索:

初始状态,从顶点1开始

依次访问过顶点1,2,3后,终止于顶点3(再次提醒,4->3)

从顶点3回溯到顶点2,继续访问顶点5,并且终止于顶点5

从顶点5回溯到顶点2,并且终止于顶点2

从顶点2回溯到顶点1,并终止于顶点1

从顶点4开始访问,并终止于顶点4

三、模板

 

  1.深搜

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
char map[1000][1000];//用来存储图的信息

/*标记是否搜索过,本题目没有使用,因为是一次访问,直接将访问过的修改即可*/
int logo[1000][1000];

int m,n,sum;

/*表示八个方向,四个方向时,将后面四组删掉就可以了*/
int dir[8][2]= {0,1, 0,-1, 1,0, -1,0, 1,1, 1,-1, -1,1, -1,-1};

void DFS(int x,int y)
{
    if(x>=0&&y>=0&&x<n&&y<m)//这里是判断是否越界,根据题目要求改写
    {
        if(map[x][y]=='W')//如果符合条件就继续递归。
        {
            map[x][y]='.';//标记为‘.’防止多次访问
            for(int i=0; i<8; i++)//因为八个方向,所以循环八次。
                DFS(x+dir[i][0],y+dir[i][1]);
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        sum=0;
        memset(logo,0,sizeof(map));
        getchar();
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                cin>>map[i][j];
            }
            getchar();
        }
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
                if(map[i][j]=='W')
                {
                    DFS(i,j);
                    sum++;//计数
                }
        }
        cout<<sum<<endl;
    }
}

 

  2.广搜

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define MAX 0x3f3f3f3f
using namespace std;
int map[305][305];//存节点信息
int vis[305][305];//标记数组
int dir[4][2]= {-1,0, 1,0, 0,1, 0,-1};//上下左右四个方向
int end;
struct node
{
    int x,y;//两点表示节点位置
    int time;
} start;//入队列使用
queue<node> q;//队列,自己维护用来存储节点信息
int bfs(int x,int y)
{
    memset(vis,0,sizeof(vis));
    start.x=x,start.y=y,start.time=0;//将传递过来的0.0节点放入结构体
    vis[x][y]=1;//标记为已搜过
    q.push(start);//入队列
    while(!q.empty())
    {
        node now=q.front();//取队头元素
        q.pop();
        if(map[now.x][now.y]==MAX)
        {
            return now.time;//如果符合条件,返回;根据题意自己写符合的条件。
        }
        for(int i=0; i<4; i++)//四个方向入队列
        {
            start.x=now.x+dir[i][0],start.y=now.y+dir[i][1];//将第一个方向的入队列
            start.time=now.time+1;
            if(start.x>=0&&start.y>=0&&vis[start.x][start.y]==0&&start.time<map[start.x][start.y])//判断是否越界
            {
                vis[start.x][start.y]=1;
                q.push(start);
            }
        }
    }
    return -1;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(map,MAX,sizeof(map));
        for(int j=0; j<n; j++)
        {
            int x,y,time;
            scanf("%d%d%d",&x,&y,&time);
            if(map[x][y]>time)
                map[x][y]=time;
            for(int i=0; i<4; i++)//自己建图过程,一般不需要自己建图
            {
                int cx,cy;
                cx=x+dir[i][0],cy=y+dir[i][1];
                if(cx>=0&&cy>=0)
                    if(map[cx][cy]>time)
                        map[cx][cy]=time;
            }
        }
        int ans=bfs(0,0);//从00点开始广搜,根据题目要求具体定
        cout<<ans<<endl;
    }

}

 

posted @ 2017-08-01 15:09  爱国呐  阅读(4716)  评论(0)    收藏  举报