BFS & DFS的基础学习

来源:互联网 发布:什么时候跑步最好知乎 编辑:程序博客网 时间:2024/06/06 00:19

只是对BFS和DFS一个简单的介绍。

简而言之,bfs和dfs 是对于在树或者图之类结构上的数据进行搜索得出想要的答案的搜索方法。事实上很多ACM题目中都会用到,哪怕看似是纯数学的题目,但是我们可以人工抽象成树或者图来运用。如果把搜索对象范围比喻成为一个多分支的洞穴的话,dfs就是放一只由程序跟踪的老鼠进去,走到每个尽头再返回继续探索下一个,而bfs就是从洞口向里面倒由程序跟踪的水,同步进行到各个分枝洞穴。
1.关于DFS。
其函数常常会是这样的:

void dfs(int start){    if(no)        return;    Dosomethinginteresting();    for(i=0;i<n;i++)        if(ok)            {                // visited=1;                dfs(next);                // visited=0;            }}
其实际执行情况就如图。红色箭头就表示dfs程序的执行过程。对于深搜dfs,具体描述应该是:
(1)指定一个当前节点,标记已经访问。
(2)把当前节点的每个未被访问的子节点当做当前节点进行递归,执行(1)。
(3)不满足条件时进行回溯。

由于深搜在进行过程中都是沿着数据结构的指定方向前进的,所以能够很清楚的记录下来走过的路以及走过的时间。但是,在数据量太大的时候,由于用的是递归的单线搜索,很容易出现运行时间很长的情况。而对于求最短路径的问题,dfs需要枚举所有可能的路径然后进行比较,过程会比较繁琐。

为了使得dfs运行时间更快,有一个常用方法那就是剪枝。剪枝在程序里面的体现往往就是一个if(no) break; 或者if(no) return;剪枝规避了一些不符合要求的深搜路径使得程序不会去走那些已经知道是错误的路。比如有时候题目要求在偶数步数走到一个距离奇数步数的节点,这是不可能的,所以不需要进行任何深搜。
另外,记忆化也是一个有效的改进方法。对于在一个相互连接的图样结构中,各个路径有可能访问相同的节点,那么为了避免走同样的路,第一次走过这个节点的时候就可以将它的值存在一个数组之中,那么就可以避免重复递归了。程序实现就是定义二维数组a[m][n],初始化全部为0。当程序走到节点(m,n)时,如果a[m][n]==0,那么把值存进去,如果不为零,直接读取使用即可。

2、关于BFS

其函数大概是这样的:

void BFS(? asd){    queue<?> a;//? 为一种数据类型    a.push(asd);    while(1)    {        ? point = a.front();        if(a yes )        {            DoSomethingExciting();            break;        }        for(i=0;i<n;i++)        if(yes){            ? fd = initial();            a.push(fd);        }        if(!a.empty())            a.pop();        if(a.empty())        {            DosometingBad();            break;        }    }}

上面的伪代码用的是队列结构来存储过程变量。也可以用数组结构来储存。

如果用数组来存储的话,这里就需要用到两个关键变量num 和 low。

low=now=0;while(1){    int th = low;    if(yes(asd[th])){        Dosomethingmagic();    break;}    for(i=0;i<n;i++)        if(yes)            asd[++now]=deal(asd[th]);    if(low>now)        break;    low++;}

期中asd[]就是类似于队列的存在。
上图蓝色箭头就是bfs的执行路径,同一水平线上的蓝色箭头是同一批次执行的。
具体来说,BFS执行过程如下:
(1)如果队列不是空的,那么弹出一个a记为当前节点。
(2)将当前节点的所有未被访问的子节点全部存入队列中。执行(1)。

广搜最适用的情况就是求最短路径的题目。注意,这个最短路径是说每一条路径没有代价的情况。由于是各个支线同步进行,它能够最快的找到目标。但是也是因为如此,它无差别记录了所有节点会占用较大的内存。另外,由于没有从遵守目标结构的顺序,所以在获取路径的时候需要额外的操作量。即每一个节点必须要存储上一个节点的位置。另外在数据量比较大的时候,应该把队列定义位全局变量,以免栈溢出。

BFS一个简单的优化就是双向广搜,在知道目标点和初始点时适用。在一个图类结构中,现在定义两个队列,一个以初始点为开始点,一个以结束点为开始点,两个队列同时进入循环。只要有一个队列为空,那么就知道无法达到,可以直接跳出循环。然而这里有一个问题就是,两个搜索指针是很难走到一起,那么我们必须知道搜索的节点是否已经被对方搜索过了,如果题目对象仅仅占用一个节点,那么标记是很简单的;如果题目对象占用多个节点或者其他复杂情况那么判断起来会有一些难度。
另外,在广搜节点的储存之前加上if判断语句来剪枝也是和dfs类似的,不再叙述。

总结:
bfs占空间,dfs占时间,这是个令人头疼的问题。但是,做搜索类题目还有一个困难就是读题。如何把题目的研究对象抽象成一段代码,如何把题目对象的研究范围抽象成为一种数据结构,我觉得这是解题的基础也是关键。


一个简单的实例:在一个长宽给定的a*b方阵中,给定开始点@,’#’代表不能走的路,只能上下左右移动,问在输入的图中最多能走到多少块砖头上。

DFS版本:

#include<iostream>using namespace std;char map[21][21]={'\0'};int wayi[4]={0,-1,0,1};int wayj[4]={1,0,-1,0};//左上右下int num=0;int a,b;int yes=1;void go(int xi,int xj){if(yes)for(int i=0;i<4;i++){if( xi+wayi[i] >= 0 && xi+wayi[i] < b && xj+wayj[i] >= 0 && xj+wayj[i] < a )if(map[xi+wayi[i]][xj+wayj[i]]!='#'){if(map[xi+wayi[i]][xj+wayj[i]]=='.')num++;map[xi+wayi[i]][xj+wayj[i]]='#';if(num==a*b)yes=0;go(xi+wayi[i],xj+wayj[i]);}}}int main(){int starti,startj;while(cin>>a>>b&&a!=0&&b!=0){num=0;yes=1;int i,j;for(i=0;i<b;i++)for(j=0;j<a;j++){cin>>map[i][j];if(map[i][j]=='@'){starti=i;startj=j;}}map[starti][startj]=',';go(starti,startj);cout<<num+1<<endl;}return 0;}

BFS版本

#include<iostream>using namespace std;char map[21][21]={'\0'};int wayi[4]={0,-1,0,1};int wayj[4]={1,0,-1,0};int num=0;int a,b;int zhizhen=0;int savei[400]={0};int savej[400]={0};int main(){while(cin>>a>>b&&a!=0&&b!=0){num=0;zhizhen=0;int i,j;for(i=0;i<b;i++)for(j=0;j<a;j++){cin>>map[i][j];if(map[i][j]=='@'){savei[num]=i;savej[num]=j;}}map[savei[num]][savej[num]]='#';while(1){int xi=savei[zhizhen],xj=savej[zhizhen];for(i=0;i<4;i++){if( xi+wayi[i] >= 0 && xi+wayi[i] < b && xj+wayj[i] >= 0 && xj+wayj[i] < a )if(map[xi+wayi[i]][xj+wayj[i]]!='#'){savei[++num]=xi+wayi[i];savej[num]=xj+wayj[i];map[xi+wayi[i]][xj+wayj[i]]='#';}}zhizhen++;if(zhizhen>num)break;}cout<<num+1<<endl;}return 0;}


2017-11-23更新:

为了给学弟更深入的理解一下DFS,BFS,双向BFS,阉割版A*的执行效果,我写了下面的代码,这个运行效果是从点x找到点y的过程。

#include <cstdio>#include <cstring>#include <stdlib.h>#include <iostream>#include <queue>using namespace std;int stx,sty;int n;int aix=0,aiy=0;struct node{    int x;int y;    node(int a = -1,int b = -1){        x=a;y=b;    }};bool operator < (const node &a,const node& b) {        return (aix-a.x)*(aix-a.x)+(aiy-a.y)*(aiy-a.y) > (aix-b.x)*(aix-b.x)+(aiy-b.y)*(aiy-b.y) ;    }int go[4][2]={{1,0},{0,1},{-1,0},{0,-1}};char grond[101][101];bool flag[101][101];void output(int lll){    if(lll==1) printf("dfs,just like a mouse in a hole\n");    else if(lll== 2) printf("bfs,just like pouring water on the grond\n");    else if (lll==3) printf("two-way bfs,just like pouring water on the grond in different position\n");    else  printf("prioritybfs,the grond is deciling\n");    grond[stx][sty]='s';    grond[aix][aiy]='e';    for(int i =0 ;i<n;i++){        for(int j = 0 ;j<n;j++){            cout<<grond[i][j];        }        cout<<endl;    }    cout<<"Made in china"<<endl;}void bfs(){//简单广搜    memset(flag,false,sizeof(flag));    queue<node> q;    q.push(node(stx,sty));    flag[stx][sty]=true;    while(!q.empty()){        node tem = q.front();        q.pop();        if(grond[tem.x][tem.y]=='e')            break;        grond[tem.x][tem.y]='a';        _sleep(5);        system("cls");        output(2);        for(int i = 0 ;i<4;i++){            int x = tem.x+go[i][0];            int y = tem.y+go[i][1];            if(x>=0 && x<n && y >=0 && y<n){                if(!flag[x][y] && grond[x][y]!='*')                {                    q.push(node(x,y));                    flag[x][y]=1;                }            }        }    }    while(!q.empty())        q.pop();}void prioritybfs(){//优先队列BFS    memset(flag,false,sizeof(flag));    priority_queue<node> q;    q.push(node(stx,sty));    flag[stx][sty]=true;    while(!q.empty()){        node tem = q.top();        q.pop();        if(grond[tem.x][tem.y]=='e')            break;        grond[tem.x][tem.y]='a';        _sleep(5);        system("cls");        output(4);        for(int i = 0 ;i<4;i++){            int x = tem.x+go[i][0];            int y = tem.y+go[i][1];            if(x>=0 && x<n && y >=0 && y<n){                if(!flag[x][y] && grond[x][y]!='*')                {                    q.push(node(x,y));                    flag[x][y]=1;                }            }        }    }    while(!q.empty())        q.pop();}void twobfs(){//双向广搜    memset(flag,false,sizeof(flag));    queue<node> q1;    queue<node> q2;    q1.push(node(stx,sty));    q2.push(node(aix,aiy));    flag[stx][sty]=true;    flag[aix][aiy]=true;    while(!q1.empty() && !q2.empty()){        node tem1 = q1.front();        q1.pop();        node tem2 = q2.front();        q2.pop();        if(grond[tem1.x][tem1.y]=='b')            break;        grond[tem1.x][tem1.y]='a';        if(grond[tem2.x][tem2.y]=='a')            break;        grond[tem2.x][tem2.y]='b';        _sleep(5);        system("cls");        output(3);        for(int i = 0 ;i<4;i++){            int x = tem1.x+go[i][0];            int y = tem1.y+go[i][1];            if(x>=0 && x<n && y >=0 && y<n){                if(!flag[x][y] && grond[x][y]!='*')                {                    q1.push(node(x,y));                    flag[x][y]=1;                }                else if(flag[x][y] && grond[x][y]=='b')//注意与另外一支汇合                    q1.push(node(x,y));            }        }        for(int i = 0 ;i<4;i++){            int x = tem2.x+go[i][0];            int y = tem2.y+go[i][1];            if(x>=0 && x<n && y >=0 && y<n){                if(!flag[x][y] && grond[x][y]!='*')                {                    q2.push(node(x,y));                    flag[x][y]=1;                }                else if(flag[x][y] && grond[x][y]=='a') //注意与另外一支汇合                    q1.push(node(x,y));            }        }    }    while(!q1.empty())        q1.pop();    while(!q2.empty())        q2.pop();}int yes;void showmessage(){cout<<"I know there is input error,but donot believe that I will solve this problem for you"<<endl;}void dfs(int x,int y){//普通深搜    if(x==aix && y == aiy){        yes =1;        return;    }    grond[x][y]='a';    _sleep(5);    system("cls");    output(1);    for(int i = 0 ;i<4;i++){        int a = x+go[i][0];        int b = y+go[i][1];        if(a>=0 && a<n && b >=0 && b<n){            if(!flag[a][b] && grond[a][b]!='*')            {                flag[a][b]=1;                dfs(a,b);                if(yes)                    return;            }        }    }}void init(){    grond[stx][sty]='b';    grond[aix][aiy]='e';    for(int i =0 ; i <n;i++)        for(int j=0;j<n;j++)        {            grond[i][j]='.';        }    for(int i  = 0;i<n-3;i++)        grond[n/2][i]='*';}int main(){    printf("please input the size:(0<n<100)");    cin>>n;    printf("start point x,y:");    cin>>stx>>sty;    printf("end point x,y:");    cin>>aix>>aiy;    if(stx<0 || sty < 0 || sty>=n || stx >=n ){        showmessage();        _sleep(1000);    }    init();    printf("now I will show you dfs,bfs,Two-waybfs and priority_bfs,press 'a' to continue;\n");    char c;    while(cin>>c){        if(c!='a')            break;        init();        yes = 0;        memset(flag,0,sizeof(flag));        dfs(stx,sty);        _sleep(500);        init();        bfs();        _sleep(500);        init();        prioritybfs();        _sleep(500);         init();        twobfs();        printf("press 'a' to once again,or any other char to exit\n");    }    return 0;}




原创粉丝点击