java广度优先算法,算法之广度优先搜索
一、引言
上一次介绍的算法是深度优先搜索
这次我们来研究一下广度优先搜索,看看怎么理解以及写出这个算法
这个算法需要数据结构的基础--队列,如果没有这个基础的同学去恶补一下。
二、小小问题
Q:在一个二维地图中,从一个点到另一个点的最短路径(从1到0,输入终点位置,输出最少步数)
1 - - - -
0 -
第一种:采用深度优先搜索呗。
第二种:用广度优先搜索。当然,你会说,我要是会广搜,我就不会看这篇文章了。
2.1 深度优先搜索
看到深度优先搜索,第一反应就是模板套出来
public static void dfs(int step){
if(...){
return;
}
for(...){
...
dfs(step+1);
...
}
}
DFS主要在于当前这步怎么做,然后继续往下,当然前提我们需要判断它这一步有没有走到终点,也就是另一个点的位置。核心的dfs方法之前一直都只是传递一个step(当前步数),现在我们需要判断这一步有没有走到终点,则需要把当前的x和y一并传递到下一个方法里面去,则dfs的方法声明如下:
public static void dfs(int x, int y , int step){
...
}
现在,dfs里面的判断是什么呢?因为dfs是基于递归,所以必定有出口,所以出口是什么呢?出口就是终点位置的x和y。当然在满足这个的情况下,我们要继续判断当前到终点的最少步数是不是比最少的步数还要小,如果还要小则更新最少的步数。
// endx是终点的x endy是终点的y
public static void dfs(int x, int y , int step){
if(endx==x && endy==y){ // 判断是否到了终点
if(step < min_step){ // 判断是否为最小步数
min_step = step
}
return; // 返回
}
for(...){
.....
dfs(....);
......
}
}
现在最重要的问题来了,for循环里面的内容如何填写呢? 因为当前的点,可以上下左右行走,则对于走向应该有四个方向,上下左右。那我们定义一个二维数组来搞定方向问题。
因为向左y-1,向右y+1,向上x-1,向下x+1
int[][] direction = {{0, -1}, {-1, 0},{0, 1}, {1, 0}};
//左,上,右,下
所以for循环里面,只需要基于当前的点(x,y)上下左右遍历即可。所以for循环里面只需要遍历方向即可。
当然也需要标记当前点是否访问,所以在之后,判断下一步的点是否访问过,没有访问,则标记访问,然后继续递归。。
for(int i = 0 ; i<4;i++){
// 计算下一个点的坐标
next_x = x + direction[i][0];
next_y = y + direction[i][1];
// 需要判断下一个点是否越界,有可能超出了边界
// n是数组的行,m是数组的列
if (next_x < 0 || next_x >= n || next_y < 0 || next_y >= m) {
continue;
}
if (result[next_x][next_y] == 0) {// 判断当前点是否访问
result[next_x][next_y] = 1; // 标记当前点访问
dfs(next_x, next_y, step + 1); // 另一个点继续递归
result[next_x][next_y] = 0; // 标记当前点未访问,基于上一步的递归结束
}
}
dfs方法的全部代码如下:
// endx是终点的x endy是终点的y
public static void dfs(int x, int y , int step){
if(endx==x && endy==y){ // 判断是否到了终点
if(step < min_step){ // 判断是否为最小步数
min_step = step
}
return; // 返回
}
for(int i = 0 ; i<4;i++){
// 计算下一个点的坐标
next_x = x + direction[i][0];
next_y = y + direction[i][1];
// 需要判断下一个点是否越界,有可能超出了边界
// n是数组的行,m是数组的列
if (next_x < 0 || next_x >= n || next_y < 0 || next_y >= m) {
continue;
}
// result是二维数组用于标记点是否访问,是全局类型
if (result[next_x][next_y] == 0) {// 判断当前点是否访问
result[next_x][next_y] = 1; // 标记当前点访问
dfs(next_x, next_y, step + 1); // 另一个点继续递归
result[next_x][next_y] = 0; // 标记当前点未访问,基于上一步的递归结束
}
}
}
基本上到这里,采用DFS解决这个问题已经完成了,还有一些输入输出,我这里就省略了,其余的就靠大家自己补充了。
2.2 广度优先搜索
Q:深度优先搜索是从某一个点出发,然后遍历方向。选定一个方向,然后再遍历方向....依次类推,直到遇到终点或者遍历完毕。那我们能不能一次性的将周围的点给遍历,然后再遍历周围的点后再得相邻的点遍历呢?重复上面的步骤,这样当遍历周围的点时,我知道是遍历第几层,也就是第几步,直到遇到终点,最少的步数得出。
比如:
(0,0)相邻的点是(0,1)和(1,0); 从(0,0)分别到这两个点的步数是1,
(0,1)相邻的点是(1,1)和(0,2),(0,0); 从(0,1)分别到这两个点的步数是2.(0,0)已经访问,此时没有必要访问,所以需要在前一步需要标记已经访问,
(1,0)相邻的点是(2,1)和(1,1),(0,0); 从(0,1)分别到这两个点的步数是2,(0,0)已经访问,此时没有必要访问,所以需要在前一步需要标记已经访问,
(1,1)相邻的点是(2,1)和(1,2),(0,1),(1,0); 从(0,1)分别到这两个点的步数是3;(0,1)已经访问,此时没有必要访问,所以需要在前一步需要标记已经访问,(1,0)已经访问,此时没有必要访问,所以需要在前一步需要标记已经访问,
A B C D
E F G H
I J K L
按照上面的规律,BFS这个二维数据输出,方向从右往左,(有些因为已经遍历便没有写出来)
基于A: ABE
基于B: CF
基于E: I
基于C: DG
基于F: J
基于I: H
基于D: K
基于J: K(已经遍历)
基于H: L
很明显,学过数据结构的可以看出来这是一个先进先出的队列结构; 所以我们的BFS是基于队列实现的。然后对于这个小问题,我们如何解决呢?
遍历每个点的时候,存储当前节点的步数以及当前点的信息(x,y)即可,然后将这个整体信息进队列,然后获取头结点的相邻数据让其进队列,如果它的相邻数据已经进队列完毕,则让头结点出队列,接着再获取头结点信息,获取头结点的相邻数据让其进队列..步骤雷同不再累述。
这里使用的LinkedList,实现了队列的基本操作。具体操作请参考Java API。
节点有几个重要属性,当前坐标x,y和当前节点是第几步可以到达,则类结构如下:
static class Node{
int x ;
int y ;
Node parent;
...省略getter/setter方法
}
// 设置队列
Queue que = new LinkedList();
//起点进队列
Node node = new Node();
node.x = startx;
node.y = starty;
node.step = 0;
node.parent = null;
que.add(node);// 起始点进队列
// 将起点的周围的点进队列
for (int i = 0; i < 4; i++) {
int tx = heads.x + direction[i][0];
int ty = heads.y + direction[i][1];
// 判断是否越界
if (tx < 0 || tx >= n || ty < 0 || ty >= m) {
continue;
}
// 判断该点是否为障碍物并且有没有被访问
if (map[tx][ty] == 0 && result[tx][ty] == 0) {
Node next = new Node();
result[tx][ty] = 1;
next.x = tx;
next.y = ty;
next.parent = heads;
next.step = heads.step + 1;
que.add(next);// 进队列
}
// 如果当前点为最终节点
if (tx == endx && ty == endy) {
break; // 退出for循环
}
}
上面只是满足了最简单的起点周围的点进队列,如果周围的点也要参照这个逻辑进行循环遍历得到相邻的点,则加一个出队列的操作循环判断队列是否还有节点即可。修改代码如下:
// 以当前点广搜
Node heads; // 头节点
Node resultNode = null;// 终点
while ((heads = que.poll()) != null) {// 判断队列是否还有元素,也就是出队列操作
for (int i = 0; i < 4; i++) {
int tx = heads.x + direction[i][0];
int ty = heads.y + direction[i][1];
// 判断是否越界
if (tx < 0 || tx >= n || ty < 0 || ty >= m) {
continue;
}
// 判断该点是否为障碍物并且有没有被访问
if (result[tx][ty] == 0) {
Node next = new Node();
result[tx][ty] = 1;
next.x = tx;
next.y = ty;
next.parent = heads;
next.step = heads.step + 1;
resultNode = next;
que.add(next);
}
// 如果当前点为最终节点
if (tx == endx && ty == endy) {
flag = 1; // 标志找到
break; // 退出for循环
}
}
if (flag == 1) // 如果找到 退出while循环
break;
}
三、概念
Q:什么是广度优先算法呢?
广度优先算法简称BFS。它主要是”一层层的“的搜索来进行扩展获取每个点的数据,直到找到某个确定的点。Java已经实现了这种数据结构--Queue,对于我们的话,直接使用即可。
四、总结- BFS数据结构是队列
BFS适用于树的高度不深,子节点的数量不多。否则耗内存存储节点数据。也就是队列数据的存储。
DFS寻找有解,很难寻找最优解;但是没有BFS的缺点,耗内存。
五、小试牛刀
海岛淹没,有多少个没有淹没的小岛?(上下左右四个相邻像素中只要有一个方向有海洋,它就会被淹没)
.......
.##....
.##....
....##.
..####.
...###.
.......
六、示例代码- - DFS
java广度优先算法,算法之广度优先搜索相关推荐
- java数据结构和算法——图的广度优先(BFS)遍历
目录 一.图的遍历介绍 二.图的广度优先搜索(Broad First Search) 三.图的广度优先遍历算法步骤 四.图的广度优先遍历示例需求 五.图的广度优先遍历代码示例 一.图的遍历介绍 所谓图 ...
- Algorithm:C++语言实现之图论算法相关(图搜索广度优先BFS、深度优先DFS,最短路径SPF、带负权的最短路径Bellman-ford、拓扑排序)
Algorithm:C++语言实现之图论算法相关(图搜索广度优先BFS.深度优先DFS,最短路径SPF.带负权的最短路径Bellman-ford.拓扑排序) 目录 一.图的搜索 1.BFS (Brea ...
- 广度优先遍历算法-01寻找制高点问题
寻找制高点 前言 广度优先遍历算法在搜索答案时不像深度优先那样一条路走到底,而是采用由近及远的方式,先访问离起始点最近的所有点,再访问远一些的点.由于这种一层层搜索的策略,又叫做层次遍历算法. 简介 ...
- 简述树的深度优先及广度优先遍历算法,并说明非递归实现?
深度优先遍历二叉树. 1. 中序遍历(LDR)的递归算法: 若二叉树为空,则算法结束:否则: 中序遍历根结点的左子树: 访问根结点: 中序遍历根结点的右子树. 2. 前序遍历(DLR)的递归算法: 若 ...
- floyd算法java_利用JAVA和Floyd算法实现上海地铁最短路线搜索系统
[实例简介] Floyd算法基于JAVA实现上海地铁最短路线搜索系统,图形界面,支持新建删除站点以及新建线路. [实例截图] [核心代码] SUBWAY └── SUBWAY ├── bin │ ...
- java沙漏_(java)五大常用算法
算法一:分治法 基本概念1.把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题--直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并.2.分治策略是对于一个规模 ...
- Java 数据结构和算法(十五):无权无向图
Java数据结构和算法(十五)--无权无向图 前面我们介绍了树这种数据结构,树是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合,把它叫做"树"是因为它看起 ...
- 数据结构 - Java -韩顺平 图解Java数据结构和算法
数据结构 Lesson 1 数据结构的知识总结 1. 几个经典的算法面试题 2. 线性结构与非线性结构 2.1 稀疏数组 sparsearray 2.2 队列 2.2.1 顺序队列: 2.2.2 环形 ...
- 一.Java数据结构与算法:如何开始
数据结构和算法是计算机科学的核心概念之一,它们在软件开发中起着至关重要的作用.学习Java数据结构和算法不仅有助于提高编程能力,还能让你在面试和职业发展中脱颖而出.本文将为你介绍数据结构和算法的重要性 ...
最新文章
- 两个变量交换值 和按位异或的理解
- webpack插件机制
- 【概率论】1-0:介绍
- 各种数据结构性能的比较
- 7-4 吃货的最短路径 (10 分)
- python面向对象三大特性_深入理解Python面向对象的三大特性
- Hexo 入门指南(四) - 页面、导航、边栏、底栏
- 项目中查询数据和模糊查询
- Markdown标题、行首缩进、换行。csdn(1)
- ABP应用层——参数有效性验证
- 数模笔记:蒙特卡洛模型
- python学习的读书路线
- ios 请在设置中打开相机权限_iOS关于相机相册权限设置
- 2022云管和云网大会|《云容灾白皮书2022》发布,万博智云参与撰写
- Windows Server 2012 之NIC组合(NIC Teaming)介绍
- 读书笔记 - 《枪炮、病菌与钢铁》
- He initialization
- grpc-gateway插件:让客户端通过调http接口来远程调用grpc服务
- 线代[4]|浅谈数域
- 铁路计算机联锁系统的设计方案,铁路信号计算机联锁系统的研究与设计
热门文章
- Not Found - GET https://registry.npmjs.org/@vue%2fvue-loader-v15 - Not found
- DAM | 提升品牌自播引流能力的「幕后」推手 | 数字内容管理 引流营销资源
- 会签2:会签的主要后台JAVA代码
- 一元二次方程java实现代码
- 支付宝手机网页即时到账接口(3)之交易接口请求
- gis栅格镶嵌时候出错
- 2022年甘肃省职业院校技能大赛“网络搭建与应用”赛项
- Base64与Bitmap转换
- matlab粒子群加约束条件_多目标粒子群(PSO)与MATLAB程序视频教程及动态优化问题约束条件...
- Openlayers:点聚合效果