相信我,看完之后,你会对BFS有种全新的了解,彻底掌握BFS
只需要这一篇就足够啦,狗头

BFS路径表示

广度优先搜索顾名思义就是以迷宫里的无向图某一个点,借助队列,一层一层以该点为中心散开进行搜索,简单的BFS只能显示出最短路径的长度,这里探讨的就是如何实现BFS对最短路径的输出

简单的BFS

通过队列来实现,找到迷宫的起点(S)入队,出队列里面的队首,把队首上下左右相邻的点入队列,一直重复此操作,直到队列里面的所有元素都出队表示该迷宫不存在解,如果在队清空之前找到终点(T)的话,一层一层的遍历,找到的话就一定是最短路了,但是简单的BFS只能通过输出(now.d+1)来表示最短路的步数长度,不能表示出最短路径到底是什么?

#include <iostream>
#include <string>
#include <queue>
using namespace std;
int n, m;//地图长宽
string maze[110];//地图
bool vis[110][110];//访问标记数组
int dir[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};//方向变化
bool in(int x, int y) {//确保在地图里面return 0 <= x && x < n && 0 <= y && y < m;
}
struct node{//定义node类型的结构体int x,y,d;//d表示路径node(int xx,int yy,int dd){//便于输入结构体里面的值x=xx;y=yy;d=dd;}
};
int bfs(int sx,int sy){queue<node>q;q.push(node(sx,sy,0));//将起点放入vis[sx][sy]=true;while(!q.empty()){//队列如果不为空继续node now=q.front();//找到队首q.pop();//队首出队for(int i=0;i<4;i++){int tx=now.x+dir[i][0];//周围遍历int ty=now.y+dir[i][1];if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){//在界内且未遍历到if(maze[tx][ty]=='T'){//如果是终点,BFS返回完成return now.d+1;}else{//如果不是不返回,标记该点入队继续搜索vis[tx][ty]=true;q.push(node(tx,ty,now.d+1));}}}}return -1;//队空未找到就返回-1表示无解
}
int main() {cin >> n >> m;for (int i = 0; i < n; i++) {cin >> maze[i];}int x, y;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (maze[i][j] == 'S') {//找到迷宫的起点x = i, y = j;}}}cout<<bfs(x,y)<<endl;return 0;
}

我下午问了杰哥一下,杰哥给我介绍了两种思路下面的三种方法
真的杰哥这些思路都是很经典很厉害的思想

思路一

就直接引用原话啦

我看着杰哥的代码理解了一下,大概意思就是
1.首先正着从起点进行一遍BFS找到终点,再从终点反正来一遍BFS找到起点
2.开创俩个数组dis1[N][N]和dis2[N][N]分别用于记录正向搜索每一次的出队时的每一个离起点的层数和反向搜索每一次的出队时的每一个点离起点的层数
3.此时通过BFS我们已经知道最短路径的长度了,某个点只要能满足
dis1[x][y]+dis2[x][y]=minrode
那么这个点就一定是这个迷宫最短路径上面的点
4.通过这样我们就能知道该迷宫的最短路径表示

代码展示如下:

include<queue>
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1005;//地图最大宽度
struct node{int x,y;
};
queue<node>que;
int n,m,minrode=0;
int fx,fy;//记录终点的坐标值
char map[N][N];
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
int dis1[N][N];//正向记录层数的数组
int dis2[N][N];//逆向记录层数的数组
bool mark[N][N];//用来标记是不是在最短路上 void BFS(int dis[][N],int sx,int sy){for(int i=0;i<n;i++)//初始化记录层数的dis数组 for(int j=0;j<m;j++)dis[i][j]=-1;while(!que.empty())que.pop();//初始化队列 que.push((node){sx,sy});//放入起点 dis[sx][sy]=0;//(sx,sy)为起点 while(!que.empty()){node cur=que.front();//当前状态que.pop();for(int i=0;i<4;i++){node nxt;nxt.x=cur.x+dx[i];nxt.y=cur.y+dy[i];if(map[nxt.x][nxt.y]=='#'){//墙壁无法通行 continue;} if(nxt.x>=0&&nxt.x<n&&nxt.y>=0&&nxt.y<m&&dis[nxt.x][nxt.y]==-1){if(map[nxt.x][nxt.y]=='T'){fx=nxt.x;fy=nxt.y;minrode=dis[cur.x][cur.y]+1;return ;}else{que.push(nxt); dis[nxt.x][nxt.y]=dis[cur.x][cur.y]+1;//不断的记录层数}}}}
} void Print(int x,int y){//对起点进行上下左右找,满足的mark=true的进行输出printf("%d %d\n",x,y);    for(int i=0;i<4;i++){//同样的进行遍历判断 int nx=x+dx[i];int ny=y+dy[i];if(nx<0||nx>=n||ny<0||ny>=m||map[nx][ny]=='#')continue; if(dis1[nx][ny]<dis1[x][y])continue;//***不能往回走不能成环 if(mark[nx][ny]){Print(nx,ny);break;//避免输出多条最短路 }}
}int main(){int qx,qy;scanf("%d%d",&n,&m);for(int i=0;i<n;i++){scanf("%s",map[i]);}for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (map[i][j] == 'S') {//找到迷宫的起点qx = i;qy = j;}}}BFS(dis1,qx,qy);//正向遍历 BFS(dis2,fx,fy);//逆向遍历 memset(mark,0,sizeof(mark));//初始化最短路径记录数组 int total=minrode;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(dis1[i][j]+dis2[i][j]==total){ mark[i][j]=true;//如果符合的话就是最短路,mark数组该点标记为true }}}Print(qx,qy);//调用print函数进行打印mark上面为true的点 return 0;
}

重点来了,关键关键关键!!!

if(dis1[nx][ny]<dis1[x][y])continue;//***不能往回走不能成环

这一步真的print函数的重点所在
为什么能打印满足mark数组等于true的点,我们要知道一个迷宫不只有一条最短路,有很多点都满足这个这个条件,那么万一出现这种成环的情况怎么办?

按照print函数层次遍历的算法,在A点可以会向右跑了,这样根本不是最短路,但是我们知道按照最短的遍历,dis1数组上面符合最短路的下一步的值一定会比这一步要大,我们在这里就可以做出限制条件,这样一方面就避免了往回回溯的可能,也避免了成环跑掉的可能,如果最短路不成环的话,我们也可以对已经打印出来的点进行标记,标记后不能打印,这样也可以实现避免了往回回溯的可能。

思路二

照片如图

这种思路大概就很像树型结构的寻找父亲,保存此时的坐标的信息和上一个坐标的信息,从终点往回不断的找,找父亲的父亲的父亲…直到起点为止,这样找下来也是一种最短路,像我这样就只能想到一些笨办法
第一种就是直接建立树,这个树结点不仅保存该点的值,还保存下面儿子的地址和上面父亲结点的地址,这样一来直接就可以通过树来找到了,要写树代码量真的太大了,舍去。。。
第二种就是建立结构体包含四个值,自己的XY坐标值,父亲的XY坐标值,从结点开始利用父亲的XY值遍历整个序列,看谁的XY和这里的对应就找到了父亲结点,依次进行,但是这样的复杂度太高了吧。。。果断舍去。。。
好的,下面就介绍杰哥给的几种复杂度低的算法

算法一

这样怎么说呢,有点类似树的双亲孩子表示法,建立一个数组,里面装着每一个结点,结点后面跟着父亲结点在数组里面的下标值,就是一个结构体数组,通过每一个结点里面的父亲结点在数组里面的下标值就可以找到父亲结点了,通过在数组里面存储下标来回溯,很棒呀。

代码如下:

/*Input:默认以(0,0)为起点,(4,4)为终点,随便改一下就行
5 5
...##
##.##
....#
.##.#
.....*/
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1005;
struct node{int x,y,lastId;
}que[N*N];//最大有N*N个结点,没有墙全是路 int n,m;
char map[N][N];int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};void Print(node cur){if(cur.lastId!=-1){ Print(que[cur.lastId]);//递归的算法,先找到第一个结点后输出值}printf("%d %d\n",cur.x,cur.y);//先找后输出就是顺序,先输出后找就是逆序
}int dis[N][N];
void BFS(){memset(dis,-1,sizeof(dis));int L=1,R=0;que[++R]=(node){0,0,-1};//lastId=-1表示没有父亲 dis[0][0]=0;//(0,0)为起点 while(L<=R){node cur=que[L++];//当前状态for(int i=0;i<4;i++){node nxt;nxt.x=cur.x+dx[i];nxt.y=cur.y+dy[i];nxt.lastId=L-1;//当前状态的上一个状态在que数组中的下标为L-1 if(nxt.x==n-1&&nxt.y==m-1){//(n-1,m-1)为终点 Print(nxt);return; }if(map[nxt.x][nxt.y]=='#'){//无法通行 continue;} if(nxt.x>=0&&nxt.x<n&&nxt.y>=0&&nxt.y<m&&dis[nxt.x][nxt.y]==-1){que[++R]=nxt; dis[nxt.x][nxt.y]=dis[cur.x][cur.y]+1;}}}
} int main(){scanf("%d%d",&n,&m);for(int i=0;i<n;i++){scanf("%s",map[i]);}BFS();return 0;
}

算法二

我真的有点被这个算法惊讶到了
利用二维数组可以存储四个值,一般来数二维数组值可以存储三个值(x,y,本身点的信息),但是这里可以使用两个二维数组存储x,y,fx,fy的信息,分而和,和而找,既然知道了这四个信息,我们也就不难去回溯了
代码如下:

#include<queue>//还是和上面一样,(0,0)起点,(n-1,m-1)终点
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1005;struct node{int x,y;
};int fax[N][N];
int fay[N][N];queue<node>que;//对列 int n,m;
char map[N][N];int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};int dis[N][N];void Print(int x,int y){if(x!=-1&&y!=-1){Print(fax[x][y],fay[x][y]);printf("%d %d\n",x,y);}
}void BFS(){memset(dis,-1,sizeof(dis));que.push((node){0,0});dis[0][0]=0;//(0,0)为起点 fax[0][0]=-1;fay[0][0]=-1;while(!que.empty()){node cur=que.front();//当前状态que.pop();for(int i=0;i<4;i++){node nxt;nxt.x=cur.x+dx[i];nxt.y=cur.y+dy[i];if(map[nxt.x][nxt.y]=='#'){//无法通行 continue;} if(nxt.x>=0&&nxt.x<n&&nxt.y>=0&&nxt.y<m&&dis[nxt.x][nxt.y]==-1){que.push(nxt); dis[nxt.x][nxt.y]=dis[cur.x][cur.y]+1;fax[nxt.x][nxt.y]=cur.x;fay[nxt.x][nxt.y]=cur.y;}}}
} int main(){scanf("%d%d",&n,&m);for(int i=0;i<n;i++){scanf("%s",map[i]);}BFS();Print(n-1,m-1);return 0;
}

其他的简单的BFS一样,最为精华的我展示出来了

void Print(int x,int y){if(x!=-1&&y!=-1){Print(fax[x][y],fay[x][y]);printf("%d %d\n",x,y);}
}

难以言语,分开来,合起来,分开来,再合起来…
道家的思想,
**分而治之,和而为之 **

嗯嗯,就介绍完啦,你是否got到啦

是不是肥肠的斯国一

感谢杰哥这么细心的指导

我终于也掌握BFS啦

哈哈哈哈

广度优先搜索(BFS)最短路径输出表示(三种方法)相关推荐

  1. 汇编输出12ABH三种方法

    用mov ah,02h 输出12ABH 我会三种方式,想看看有没有大佬有更容易的方法 第一种: data segmentBUF dw 12ABH ;定义数据 data ends code segmen ...

  2. 占位符及格式化输出的三种方法

    1.直接占位符 这是最常见的输出,要注意用到+时,整形的a要转成字符串才能输出 a = 2 b = "yym" print(" I'm " + b + &quo ...

  3. Tomcat-常用配置文件及关闭或调整catalina.out日志输出的三种方法

    一.常用配置文件 /server/tomcat/conf/server.xml /server/tomcat/bin/catalina.sh /server/java/jdk1.7.0_51/jre/ ...

  4. 字符串倒序输出的三种方法-java版

    目录 法一(拆分字符数组): 法二(利用字符串串联符号"+"特性): 法三(StringBuilder的reverse()方法): 法一(拆分字符数组): public class ...

  5. 关于倒序输出的三种方法

  6. 【C语言】输出闰年的三种方法

    系列文章目录 C语言基础整体框架(二)http://t.csdn.cn/QyW6l C语言基础整体框架(二)http://t.csdn.cn/BqPr5 猜数字游戏--分支循环 http://t.cs ...

  7. 使用广度优先搜索找到最短路径

    by Sachin Malhotra 由Sachin Malhotra 使用广度优先搜索找到最短路径 (Finding Shortest Paths using Breadth First Searc ...

  8. 广度优先搜索BFS进阶(一):多源BFS、优先队列BFS、双端队列BFS

    一.多源BFS 在上一篇博客:广度优先搜索BFS基础中,我们接触到的BFS均是单起点(单源)的,但是对于某一些问题,其有多个起点,此类问题我们称为多源BFS问题.先思考下面一道例题: 1.腐烂的橘子 ...

  9. 广度优先搜索(BFS)——抓住那头牛(POJ 4001)

    本文将以(POJ 4001)抓住那头牛 为例,讲解经典算法广度优先搜索(BFS)的STL写法 在实际写算法中,怎么能不使用更快.更方便.更准确.更高效的C++ STL模板呢 相信很多人都了解过广度优先 ...

最新文章

  1. GitHub移动端正式发布
  2. 再谈HTTP2性能提升之背后原理—HTTP2历史解剖
  3. SQL SERVER 数据库实用SQL语句
  4. Selenium3+python自动化008-操作浏览器基本方法
  5. 碗都交出去了,能不能分到羹?
  6. 通过PowerDesigner实现sql-pdm-sql
  7. Asp.net動態添加控件(转)
  8. 《互联网+流通——F2R助力传统产业创新与转型》一一第1章 “互联网+”的新时代...
  9. python 描述_python描述符
  10. python最简单的账号密码验证_Python之简单的用户名密码验证
  11. python sql查询返回记录_干货!Python与MySQL数据库的交互实战
  12. raster | R语言中的空间栅格对象及其基本处理方法(Ⅰ)
  13. kotlin 扩展函数_在 Kotlin 中“实现”trait/类型类
  14. HTTPS加密原理(转)
  15. Struts2之类型转换中的错误
  16. 访问虚拟机web应用程序
  17. python shell清屏指令_Python Shell 怎样清屏?
  18. wⅰndows办公软件2003,办公软件2003官方下载-Office 2003官方下载免费完整版-华军软件园...
  19. 新手小白学JAVA-显示系统中文件的后缀名
  20. 【Java】面向对象编程题

热门文章

  1. 解决 CLion 出现中文乱码问题【亲测有效】
  2. 机械螺旋缠绕法管道非开挖修复
  3. iOS中常用的几种延时加载/执行的处理办法
  4. 树莓派上云-天工物接入
  5. vlc-android源码git下载
  6. 利用Python对Excel按列值筛选并拆分表格到多个文件
  7. 又是一道题拉开差距!IOI落幕,中国队三金一银,美国华人选手再获冠军
  8. 【动态规划】SSL_1322 清兵线
  9. 移动开发中的仿真器(Emulator)与模拟器(Simulator)
  10. 前端调用第三方接口跨域问题(淘宝)