【2018.07.30】(广度优先搜索算法/队列)学习BFS算法小计

一些BFS参考的博客:

https://blog.csdn.net/ldx19980108/article/details/78641127

https://blog.csdn.net/u011437229/article/details/53188837

https://blog.csdn.net/m0_37316917/article/details/70879977

关于queue头文件常用函数的解释:

https://www.cnblogs.com/xuning/p/3321733.html

 具体内容等做到了慢慢写,现在还在研究queue

迷宫问题

定义一个二维数组: 

int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};

它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

Input

一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。

Output

左上角到右下角的最短路径,格式如样例所示。

Sample Input

0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

Sample Output

(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <stack>
#include <queue> 
using namespace std;
/*做这题有了很多新感悟,如果是要探求一共有几种解法的话,那么我应该使用DFS算法,
但要是求最短路径的话,BFS算法是按层算的,所以它能更容易算出最短路径*/
/*这道题有个令人舒服的操作就是讲队尾的数据不断抽出(因为有个指针不断指向父节点)
然后放入堆栈中,再一个个从堆栈顶部抽出,所以我最后得出的数据要是最后的指针*/
int map[6][6], newx[4]={-1,1,0,0}, newy[4]={0,0,-1,1};
bool sign[6][6];
class location
{
    public:
        int x,y;//坐标 
        location* father;//指向父节点的位置 
};

location* bfs()
{
    int x, y;
    queue<location*> Q;//建立关于location指针的队列
    location* begin = new location();//【重要】这里需要new一个空间来放置新数据 
    begin->x = 1;    begin->y = 1;   begin->father = NULL; 
    //初始化起点,其父节点为空,要放在函数内创建队列【重要】 
    Q.push(begin);//放入队列的第一个点 
    while ( !Q.empty() )//这里是队列和堆栈常用的判断方式,若为空队列则返回1 
    {
        location* linshi = Q.front();
        Q.pop();//获得第一个地址后就可以将它从队列里拉出来了
        for ( int i=0 ; i<4 ; i++ )//开始了哦,四方遍历 
        {
            x = linshi->x + newx[i];
            y = linshi->y + newy[i];
            if ( x<1 || x>5 || y<1 || y>5 )    continue;
            //超出边界,判断下一个点吧
            if ( sign[x][y] == true ) continue;
            //如果这个点已经走过了,我也不会再回头
            if ( map[x][y] == 1 ) continue;
            //如果有墙壁的话,我当然不会傻傻撞上去的啦
            //经过上面的三连判断,终于,这个点是可以走过去的 
            location* NEW = new location();
            NEW->x = x;    NEW->y = y;    NEW->father = linshi; 
            //建立一个新指针,将这个可行点代入数值 
            if ( x==5 && y==5 )    return NEW;
            //已经达到终点了哦,把这个地址带回去吧
            Q.push(NEW);//不满足上面这个条件呀,那就将这个点放入队列之中吧
            sign[x][y] = true;//本点到此一游,来过了哦 
        }
    }
}

int main()
{  
    for(int i=1;i<=5;i++)
    {
        for(int j=1;j<=5;j++)
         {
             cin>>map[i][j];//初始化迷宫
             sign[i][j]=false;//重置sign数组 
        }      
    }
    location* p=bfs();//将最后得到的地址赋给p
    stack<location*> S;
    while ( p != NULL )//只要指针不为空的话 
    {
        S.push(p);
        p = p->father;
    }
    while( !S.empty() )//只要堆栈不为空的话 
    {
        location* temp = S.top();//依旧是新建一个指针,其等于堆顶的地址 
        S.pop();//【得到它的信息便可以删除啦】 
        cout<< "(" << temp->x-1 << ", " << temp->y-1 << ")" <<endl;
    }
    return 0;
}

这些代码的灵感很多来自于下面的代码,【】中做的是我的注释

#include<iostream>
#include<queue>
#include<stack>
using namespace std;
int migong[5][5];//保存迷宫图
bool flag[5][5];//标记改点是否到达过
class Stat
{
    public:
        int x,y;
        int t;
        Stat* father;//指向其父状态,用于逆向寻找路径
};
int R[4][2]={
    -1,0,
    1,0,
    0,-1,
    0,1
};//用于状态扩展
Stat* BFS()//返回终点状态
{
     queue<Stat*> Q;//用于存储还没有被扩展的(即将扩展)状态//【指向这个位置】 
     int x=0,y=0,t=0;
     Stat* start = new Stat();//这里是我栽过大坑,由于是在函数内部定义的对象,必须要用new来分配内存与堆中
     start->x=start->y=0;//【start的位置为(0,0)】 
     start->t = 0;
     start->father = NULL;//开始状态没有父状态
     //【上面这几句话是初始点的初始化】 
     Q.push(start);//【这里是放入第一个点的意思】 
     while( Q.empty() == false )//直到搜索完所有状态退出循环
     {
         Stat* temp = Q.front();//【新建一个临时的指针指向队头】 
         Q.pop();//【删除队头,虽然是删除队头,但是这个指针本身并未消失,只是被移出】 
         for(int i=0;i<4;i++)//这里就是状态的扩展,向上下左右四个方向扩展
         {
             x = temp->x + R[i][0];
             y = temp->y + R[i][1];    
             if(x<0||y<0||x>4||y>4)//超出边界,便直接舍弃该状态
                 continue;
            if(flag[x][y] == true)//到达过该状态,也直接舍弃
                continue;
            if(migong[x][y] == 1)//没有路,也直接舍弃
                continue;
            //【上面这三连判断的代码很酷,很对我口味】 
            Stat* tempS = new Stat();//创建新状态
            tempS->x = x;
            tempS->y = y;
            tempS->t = temp->t+1;//时间加一
            tempS->father = temp;//指向父节点
            //【上面这段作用主要是将可行点放入队列中】 
            if( x == 4 && y == 4 )
            {
                return tempS;//搜索到了终点,便返回
            }
            Q.push(tempS);//将新状态加入队列中
            flag[x][y] = true;//标记该状态已经到达过    
         }
         //【这段for循环后,刚开始进入的点的所有可能性都放入队列中,准备进入下一
        //个阶段,继续检索这个队列】 
     }
     return start;
}
 
int main()
{
    for(int i=0;i<5;i++)//循环输入迷宫
    {
        for(int j=0;j<5;j++)
        {
            cin>>migong[i][j];
            flag[i][j] = false;//全部重置为未标记 
        }
    }
    Stat* p = BFS();
    stack<Stat*> S;//声明堆栈S 
    //放入栈中,主要是为了让其反序,不然从目标状态找其父节点遍历的话,是反的
    //【这个想法很好,不这么做的话从队列里先出来的是终点】 
    while(p != NULL)// 
    {
        S.push(p);
        p = p->father;    
    }
    while(S.empty() == false)
    {
        Stat* temp = S.top();
        S.pop();//【得到它的信息便可以删除啦】 
        cout<< "(" << temp->x << "," << temp->y << ")" <<endl;
    }
}

猜你喜欢

转载自www.cnblogs.com/mokou/p/9391271.html