基础算法 之 BFS & DFS

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/admintan/article/details/78523900

Algorithm

BFS & DFS

本篇文章讨论一个最基础寻路场景 迷宫 在DFS BFS 中怎样求解
基础数据:

0#000000#*#########
0###0#00#0000000000
000#0#00##########0
#0000#00#0000000000
######00#0000######
00000000#0000000000
0000#00##########00
0000#00000000000000
0000#00000000000000
0000#00000000000000
0000#00000000000000
0000#0#############
0000000000000000000
0000000000000000000
0000000000000000000
0000000000#########
0000000000##0000000
00000#######0000000
0000000000000000000
0000000000000000000

此图为一个 202 大小的图 以任意坐标如x=0,y=0则为从左上角开始出发 符号#则为不能穿过
开始 直到寻找到*为结束 最后将路径输出为符号 (.)

基础框架

节点类

class node
{
public:
    int x,y;
    int count;
    node():x(0),y(0),count(0){};
    node(int a,int b,int c){x=a;y=b;count=c;}

};

访问标记

如果图为二维数组的话 一般需要设置一个同样大小的访问标记图 以判断该点是否被走过了从而避免向回扫描 或者重复走
以一个布尔数组来定义

bool visited[mapsize][mapsize]

扫描方向

在一般的题目中可能出现的有两种形式即四方向 上下左右

int dir[4][2]={0,1,0,-1,1,0,-1,0}; //方向

和八方向 上下左右 以及偏方向 上左…下右

int dir[8][2]={0,1 ,0,-1 ,1,0, -1,0  
             -1,-1 ,-1,1 ,1,1, 1,-1};

或者

int dx[8]={0,0,1,1,1,-1,-1,-1};
int dy[8]={1,-1,1,0,-1,1,0,-1};

约束条件

此部分是为了防止访问超出边界或者图中的阻隔元素。

bool check(int x,int y)
{
    if(x<0||x>=mapsize||y<0||y>=mapsize||map[x][y]=='#'||vis[x][y]==true||map[x][y]==0)
    return true;
    else return false;
}

DFS 递归 与 栈

DFS

使用栈来保存未被检测的点 节点按照深度优先次序被访问并依次入栈 并以相反的次序出栈进行新的检测

void DFS2(node s)//非递归DFS
{
    cout<<"______________DFS___________"<<endl;
    stack <node> p;
    p.push(s);
    vis[s.x][s.y]=true;
    while(!p.empty())
    {
        node now = p.top();
        //现在的位置是刚刚被推进栈顶的位置 由于先进后出所以向更深处搜索
        p.pop();//出队一个元素
        if(map[now.x][now.y]=='*')//结束条件
        {
        cout<<" path count is "<< now.count<<endl;
        exit(0);}
        for(int i=0;i<8;++i)
        {
            if(check(now.x+dx[i],now.y+dy[i]))
            continue;//如果路径不合法跳过之后循环内的代码执行下一次循环
            p.push(node(now.x+dx[i],now.y+dy[i],++now.count));
            vis[now.x+dx[i]][now.y+dy[i]]=true;//标记走过
            path[now.x+dx[i]][now.y+dy[i]]='.';//路径
            //输出显示

            showpath();
        }
    }
}
void DFS1(node s)//递归DFS
{
    cout<<"____________________DFS____________"<<endl;

    vis[s.x][s.y]=true;//标记初始点走过

    if(map[s.x][s.y]=='*')
    {
        cout <<"Find *& End count is"<<s.count<<endl;
        system("pause");
        //exit(0);
    }
    for(int i=0;i<8;++i)
    {
        if(check(s.x+dx[i],s.y+dy[i]))
        continue;
        vis[s.x+dx[i]][s.y+dy[i]]=true;//标记走过
        path[s.x+dx[i]][s.y+dy[i]]='@';//路径

        showpath();
        cout<< " i= "<<i<<" path count "<<s.count
        <<" x "<<s.x+dx[i]<<" y "<<s.y+dy[i]<<endl;
        DFS1(node(s.x+dx[i],s.y+dy[i],s.count+1));

    }

}

我们来看一看DFS的搜索过程

0#00..00#*#########  path
.###.#.0#0000000000  path
...#.#.0##########0  path
#....#.0#0000000000  path
######.0#0000######  path
000000.0#0000000000  path
0000#0.##########00  path
0000#00000000000000  path
0000#00000000000000  path
0000#00000000000000  path
0000#00000000000000  path
0000#0#############  path
0000000000000000000  path
0000000000000000000  path
0000000000000000000  path
0000000000#########  path
0000000000##0000000  path
00000#######0000000  path
0000000000000000000  path
0000000000000000000  path

0#00..00#*#########  path
.###.#.0#0000000000  path
...#.#.0##########0  path
#....#.0#0000000000  path
######.0#0000######  path
000000.0#0000000000  path
0000#0.##########00  path
0000#0..0.000000000  path
0000#0......0000000  path
0000#0......0000000  path
0000#0......0000000  path
0000#0#############  path
0000000000000000000  path
0000000000000000000  path
0000000000000000000  path
0000000000#########  path
0000000000##0000000  path
00000#######0000000  path
0000000000000000000  path
0000000000000000000  path

0#00..00#.#########  path
.###.#.0#..........  path
...#.#.0##########.  path
#....#.0#000.......  path
######.0#000.######  path
000000.0#000.......  path
0000#0.##########..  path
0000#0..0.0.0.0.0..  path
0000#0.............  path
0000#0.............  path
0000#0.............  path
0000#0#############  path
0000000000000000000  path
0000000000000000000  path
0000000000000000000  path
0000000000#########  path
0000000000##0000000  path
00000#######0000000  path
0000000000000000000  path
0000000000000000000  path

 i= 1 path count 87 x 9 y 0
____________________DFS____________
Find *& End count is88

BFS

BFS

使用队列保存未被检测的节点 节点按照宽度优先的次序被访问和进出队列

void BFS(node s)
{
    cout<<"______________BFS___________"<<endl;
    queue <node> q;
    q.push(s);
    vis[s.x][s.y]=true;
    while(!q.empty())
    {
        node now = q.front(); //现在的位置是刚刚被推进队列顶的位置
        q.pop();//出队一个元素
        if(map[now.x][now.y]=='*')//结束条件
        {cout<<" path count is "<< now.count<<endl;}
        for(int i=0;i<8;++i)
        {
            if(check(now.x+dx[i],now.y+dy[i]))
            continue;//如果路径不合法跳过之后循环内的代码执行下一次循环
            q.push(node(now.x+dx[i],now.y+dy[i],++now.count));
            vis[now.x+dx[i]][now.y+dy[i]]=true;//标记走过
            path[now.x+dx[i]][now.y+dy[i]]='.';//路径
            //输出显示

            showpath();
        }
    }
}

在这个执行过程中我们来看看搜索的变化范围:

0#.....0#*#########  path  
.###.#..#0000000000  path
...#.#..##########0  path
#....#00#0000000000  path
######00#0000######  path
00000000#0000000000  path
0000#00##########00  path
0000#00000000000000  path
0000#00000000000000  path
0000#00000000000000  path
0000#00000000000000  path
0000#0#############  path
0000000000000000000  path
0000000000000000000  path
0000000000000000000  path
0000000000#########  path
0000000000##0000000  path
00000#######0000000  path
0000000000000000000  path
0000000000000000000  path

0#......#*#########  path
.###.#..#0000000000  path
...#.#..##########0  path
#....#..#0000000000  path
######..#0000######  path
0.......#0000000000  path
0...#..##########00  path
0...#......00000000  path
0...#......00000000  path
0...#......00000000  path
0000#......00000000  path
0000#0#############  path
0000000000000000000  path
0000000000000000000  path
0000000000000000000  path
0000000000#########  path
0000000000##0000000  path
00000#######0000000  path
0000000000000000000  path
0000000000000000000  path

0#......#*#########  path
.###.#..#0000000000  path
...#.#..##########.  path
#....#..#..........  path
######..#....######  path
........#..........  path
....#..##########..  path
....#..............  path
....#..............  path
....#..............  path
....#..............  path
....#.#############  path
...................  path
...................  path
...................  path
..........#########  path
..........##....000  path
.....#######....000  path
.................00  path
.................00  path

可以看得出
DFS着重对深度的探索如果目标深度较高 那DFS有大概率会更高效
BFS着重对广度的探索如果目标深度较浅 或者分布分散 那BFS大概率会更高效

最终整体代码如下:


#include <iostream>
#include <cmath>
#include <fstream>
#include <string>
#include <queue>
#include <stack>
#include <string>
#include <memory.h>
#include <algorithm>
using namespace std;
#define mapsize 20

char map[mapsize][mapsize];
int dis[mapsize][mapsize];
bool vis[mapsize][mapsize];
char path[mapsize][mapsize];
int dx[8]={0,0,1,1,1,-1,-1,-1};
int dy[8]={1,-1,1,0,-1,1,0,-1};
void showmap();
void showpath();
class node
{
public:
    int x,y;
    int count;
    node():x(0),y(0),count(0){};
    node(int a,int b,int c){x=a;y=b;count=c;}

};

bool check(int x,int y)
{
    if(x<0||x>=mapsize||y<0||y>=mapsize||map[x][y]=='#'||vis[x][y]==true||map[x][y]==0)
    return true;
    else return false;
}
bool check2(int x,int y)
{
    if(x>=0&&x<mapsize&&y>=0&&y<mapsize&&map[x][y]!='#'&&vis[x][y]==false)
    return false;
    else return true;
}
// check和check2等价

void BFS(node s)
{
    cout<<"______________BFS___________"<<endl;
    queue <node> q;
    q.push(s);
    vis[s.x][s.y]=true;
    while(!q.empty())
    {
        node now = q.front(); //现在的位置是刚刚被推进队列顶的位置
        q.pop();//出队一个元素
        if(map[now.x][now.y]=='*')//结束条件
        {cout<<" path count is "<< now.count<<endl;}
        for(int i=0;i<8;++i)
        {
            if(check(now.x+dx[i],now.y+dy[i]))
            continue;//如果路径不合法跳过之后循环内的代码执行下一次循环
            q.push(node(now.x+dx[i],now.y+dy[i],++now.count));
            vis[now.x+dx[i]][now.y+dy[i]]=true;//标记走过
            path[now.x+dx[i]][now.y+dy[i]]='.';//路径
            //输出显示

            showpath();
        }
    }
}

void DFS1(node s)//递归DFS
{
    cout<<"____________________DFS____________"<<endl;

    vis[s.x][s.y]=true;//标记初始点走过

    if(map[s.x][s.y]=='*')
    {
        cout <<"Find *& End count is"<<s.count<<endl;
        system("pause");
        //exit(0);
    }
    for(int i=0;i<8;++i)
    {
        if(check(s.x+dx[i],s.y+dy[i]))
        continue;
        vis[s.x+dx[i]][s.y+dy[i]]=true;//标记走过
        path[s.x+dx[i]][s.y+dy[i]]='@';//路径

        showpath();
        cout<< " i= "<<i<<" path count "<<s.count
        <<" x "<<s.x+dx[i]<<" y "<<s.y+dy[i]<<endl;
        DFS1(node(s.x+dx[i],s.y+dy[i],s.count+1));

    }

}

void DFS2(node s)//非递归DFS
{
    cout<<"______________DFS___________"<<endl;
    stack <node> p;
    p.push(s);
    vis[s.x][s.y]=true;
    while(!p.empty())
    {
        node now = p.top(); 
        //现在的位置是刚刚被推进栈顶的位置 由于先进后出所以向更深处搜索
        p.pop();//出队一个元素
        if(map[now.x][now.y]=='*')//结束条件
        {
        cout<<" path count is "<< now.count<<endl;
        exit(0);}
        for(int i=0;i<8;++i)
        {
            if(check(now.x+dx[i],now.y+dy[i]))
            continue;//如果路径不合法跳过之后循环内的代码执行下一次循环
            p.push(node(now.x+dx[i],now.y+dy[i],++now.count));
            vis[now.x+dx[i]][now.y+dy[i]]=true;//标记走过
            path[now.x+dx[i]][now.y+dy[i]]='.';//路径
            //输出显示

            showpath();
        }
    }
}




void showmap()
{
    //display
    for(int x=0;x<mapsize;x++)
    {
    for (int y=0;y<mapsize;y++)
        cout << map[y][x] ;
        cout<< " map "<<endl;
    }
    std::cout << "\n";

}

void showpath()
{
    for(int x=0;x<mapsize;x++)
    {
    for (int y=0;y<mapsize;y++)
        cout << path[y][x] ;
        cout<< " path "<<endl;
    }
    cout<<endl;
}

int main(int argc, char* argv[])
{
    ifstream file;
    file.open("map.in");
    char ch[mapsize];
    int i=0,j=0,line=0;
    while(!file.eof()&&line<mapsize)//读图一到二十行
    {
        file>>ch;
        line++;
        for(i=0;i<mapsize;i++)
            map[i][j]=ch[i];
        j++;
        i=0;
    }
    //读图完毕
    showmap();
    node start;
   // 队列BFS
    memcpy(path,map,sizeof(map));//重新复制路径图
    memset(vis,false,sizeof(vis));//清空
    BFS(start);

    cout<< "Press any key to process DFS1"
    system("pause");

   // 递归DFS
    memset(path,0,sizeof(path));
    memcpy(path,map,sizeof(map));//重新复制路径图
    memset(vis,false,sizeof(vis));//清空
    DFS1(start);

    //栈DFS
    memset(path,0,sizeof(path));
    memcpy(path,map,sizeof(map));//重新复制路径图
    memset(vis,false,sizeof(vis));//清空
    DFS2(start);


   file.close();
   system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/admintan/article/details/78523900