BFS (Java) 广度优先搜索 简单介绍、模板、案例(一)

news2024/11/18 11:17:07

一. BFS的简单介绍

        深度优先搜索DFS和广度优先搜索BFS是经常使用的搜索算法,在各类题目中都有广泛的应用。

        深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。
        广度优先搜索算法(Breadth-First Search,缩写为 BFS),又称为宽度优先搜索,是一种图形搜索算法。简单的说,BFS 是从根结点开始,沿着树的宽度遍历树的结点。如果所有结点均被访问,则算法中止。

        一般来说,能用DFS的,一般都可以用BFS解决,反过来同理。但是不同的题目,对于DFS和BFS的复杂度可能是有些差别的,对于有些题目,用BFS更容易解决,会有更少的时间复杂度。

二. BFS模板

        BFS的载体一般选择双端队列,两边都可以增删,可以满足一些特殊的情况,比如0-1BFS(Ref.[1]).

        一般在循环体内都有另一层循环,此层循环是同层循环,一般在同层循环中,答案的更新是一样的,可以理解为树的同一层,具有同样的深度

class Solution {
    public BFS(TreeNode root) {
        //双端队列,用来存储元素
        Deque<TreeNode> queue = new ArrayDeque<>();
        //添加首个元素
        queue.add(首个元素);
        //当队列不为空一直进行循环,直到队列不再有元素
        while(!queue.isEmpty()){
            int n = queue.size();

            //得到队列的大小
            
            for(int i = 0; i < n; i++){

                var t = queue.poll();

                在同一层的操作;
                ...

            }
            在非同层更新答案;        
        }
        返回答案;
    }
}

 

三. 典型案例

1.leetcode102 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

 

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        Deque<TreeNode> queue = new ArrayDeque<>();
        if(root != null){
            queue.add(root);
        }
        while(!queue.isEmpty()){
            List<Integer> list = new ArrayList<>();
            int n = queue.size();
            for(int i = 0; i < n; i++){
                TreeNode node = queue.poll();
                 list.add(node.val);
                if(node.left != null){
                    queue.add(node.left);  
                }
                if(node.right != null){
                    queue.add(node.right);
                }
            }
            ans.add(list);
        }
        return ans;
    }
}

本题小结:(1)此题是BFS的典型题目, 在同层添加左右子节点,同层节点具有相同的深度

               (2)以颜色来区分同层,可以得到表示更为清晰的二叉树的层序遍历:

那么,原本的二叉树被分为三层,第一层{3},第二层{9,20},第三层{15,17},同一层内的操作在for循环中完成

 

2.leetcode 1091 二进制矩阵中的最短路径

给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1 。

二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:

路径途经的所有单元格的值都是 0 。
路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。
畅通路径的长度 是该路径途经的单元格总数。

输入:grid = [[0,1],[1,0]]
输出:2

 

class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        if(grid[0][0] == 1) return -1; 
        Deque<Integer> q = new LinkedList<>();
        int m = grid.length;
        int n = grid[0].length;
        boolean[] vis = new boolean[m*n];
        q.addLast(0);
        vis[0] = true;
        int ans = 0;
        while(!q.isEmpty()){
            int size = q.size();
            for(int k = 0; k < size; k++){
                int t = q.pollFirst();
                if(t == m*n-1) return ans+1;
                int x = t/n;
                int y = t%n;
                for(int i = -1; i <= 1; i++){
                    for(int j = -1; j <=1; j++){
                        int xx = x+i;
                        int yy = y+j;
                        if(xx >= m || yy >= n || xx < 0 || yy < 0 || grid[xx][yy] == 1) continue;
                        if(vis[xx*n+yy]) continue;
                        q.addLast(xx*n+yy);
                        vis[xx*n+yy] = true;
                    }
                }
            }
            ans++;
        }
        return -1;
    }
}

本题小结:(1)此题也是BFS的典型题目,此题经典的解法即为BFS或者DFS,类似的还有走迷宫的题目,在矩阵或者图中,从起点到终点的题目几乎都可以用DFS或者BFS来解决。

               (2)在同一层具有相同的深度,即在for循环内ans是相同的,在for循环外来更新ans。

               (3)来到当前点的下一个点的方向可以朝着上下左右行走,那么原本最初的for循环变为两层,但逻辑都是一样的,即:寻找下一步可以走的所有可能性。 

               (4)vis[xx*n+yy] = true;走过的进行标记,这在回溯等方法中也经常使用,在DFS、BFS此类暴力搜索的方法中也不不得不用的措施。

 

3.leetcode 1210 穿过迷宫的最少移动次数

你还记得那条风靡全球的贪吃蛇吗?

我们在一个 n*n 的网格上构建了新的迷宫地图,蛇的长度为 2,也就是说它会占去两个单元格。蛇会从左上角((0, 0) 和 (0, 1))开始移动。我们用 0 表示空单元格,用 1 表示障碍物。蛇需要移动到迷宫的右下角((n-1, n-2) 和 (n-1, n-1))。

每次移动,蛇可以这样走:

如果没有障碍,则向右移动一个单元格。并仍然保持身体的水平/竖直状态。
如果没有障碍,则向下移动一个单元格。并仍然保持身体的水平/竖直状态。
如果它处于水平状态并且其下面的两个单元都是空的,就顺时针旋转 90 度。蛇从((r, c)、(r, c+1))移动到 ((r, c)、(r+1, c))。

如果它处于竖直状态并且其右面的两个单元都是空的,就逆时针旋转 90 度。蛇从((r, c)、(r+1, c))移动到((r, c)、(r, c+1))。

 

返回蛇抵达目的地所需的最少移动次数。

如果无法到达目的地,请返回 -1。

输入:grid = [[0,0,0,0,0,1],
               [1,1,0,0,1,0],
               [0,0,0,0,1,1],
               [0,0,1,0,1,0],
               [0,1,1,0,0,0],
               [0,1,1,0,0,0]]
输出:11
解释:
一种可能的解决方案是 [右, 右, 顺时针旋转, 右, 下, 下, 下, 下, 逆时针旋转, 右, 下]。

 

class Solution {
    int r;
    int c;
    int[][] grid;
    public int minimumMoves(int[][] grid) {
        this.grid = grid;
        r = grid.length;
        c = grid[0].length;
        int ans = 0;
        int endL = r*c - 2;
        int endF = r*c - 1;
        Deque<int[]> q = new ArrayDeque<>();
        boolean[][] vis = new boolean[r*c][r*c];
        //            尾巴 头 别反了
        q.add(new int[]{0,1});
        vis[0][1] = true;
        while(!q.isEmpty()){
            for(int k = q.size(); k > 0; k--){
            var t = q.pollFirst();
            int tF = t[1];
            int tL = t[0];
            if(tF == endF && tL == endL){
                return ans;
            }
            int tFx = t[1]/c;//头x
            int tFy = t[1]%c;//头y
            int tLx = t[0]/c;//尾x
            int tLy = t[0]%c;//尾y
            //水平状态 向右平移一格 (tFx,tFy+1) (tLx,tLy+1)
            //垂直状态 向右平移一个 (tFx,tFy+1) (tLx,tLy+1)
            if(tFx == tLx){//当前是水平状态
                if(tFy+1 < c && grid[tFx][tFy+1] == 0 && !vis[tLx*c+tLy+1][tFx*c+tFy+1]){//水平向右
                    q.offerLast(new int[]{tLx*c+tLy+1,tFx*c+tFy+1});
                    vis[tLx*c+tLy+1][tFx*c+tFy+1] = true;
                }
                if(tFx+1 < r && grid[tFx+1][tFy] == 0 && grid[tLx+1][tLy] == 0){
                    if(!vis[(tLx+1)*c+tLy][(tFx+1)*c+tFy]){//水平向下
                        q.offerLast(new int[]{(tLx+1)*c+tLy,(tFx+1)*c+tFy});
                        vis[(tLx+1)*c+tLy][(tFx+1)*c+tFy] = true;
                    }
                    if(!vis[tLx*c+tLy][(tFx+1)*c+tFy-1]){//右旋转
                        q.offerLast(new int[]{tLx*c+tLy,(tFx+1)*c+tFy-1});
                        vis[tLx*c+tLy][(tFx+1)*c+tFy-1] = true;
                    }
                }
            }
            else{//当前是垂直状态
                if(tFx+1 < r && grid[tFx+1][tFy] == 0 && !vis[(tLx+1)*c+tLy][(tFx+1)*c+tFy]){//水平向下
                    q.offerLast(new int[]{(tLx+1)*c+tLy,(tFx+1)*c+tFy});
                    vis[(tLx+1)*c+tLy][(tFx+1)*c+tFy] = true;
                }
                if(tFy+1 < c && grid[tFx][tFy+1] == 0 && grid[tLx][tLy+1] == 0){
                    if(!vis[tLx*c+tLy+1][tFx*c+tFy+1]){//水平向右
                        q.offerLast(new int[]{tLx*c+tLy+1, tFx*c+tFy+1});
                        vis[tLx*c+tLy+1][tFx*c+tFy+1] = true;
                    }
                    if(!vis[tLx*c+tLy][(tFx-1)*c+tFy+1]){//左旋转
                        q.offerLast(new int[]{tLx*c+tLy,(tFx-1)*c+tFy+1});
                        vis[tLx*c+tLy][(tFx-1)*c+tFy+1] = true;
                    }
                }
            }
            }
            ans++;
        }
        return -1;
    }
}

本题小结:(1)此题是走迷宫类的题目,和上述题目不同,走的方式有一定的限制,但是总体的额方法是类似的

               (2)本解法采用水平,垂直分类处理,会出现较多的if-else,非常不美观,容易出错,但是可以明确大体思路,更简便的做法可参考Ref.[2]

参考来源 Ref.

[1] 灵茶山艾府  还在 if-else?一个循环处理六种移动!(Python/Java/C++/Go)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/700041.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习c++第01天

学习c的第01天 前言1、变量是声明&#xff1f;2.建议定义数据都对其进行初始化3.有符号数和无符号数4.进制间的相互转换5.原反补码6.const 、register 、volatile和typedef关键字7.数据类型的自动转换8.左移<< &右移操作>>9.将data的指定位数进行0、1转化的应用…

javaee 使用监听器统计当前在线用户列表

ServletContextListener 和 HttpSessionBindingListener 需要配和使用 TestServletContextListener package com.yyy.listener;import java.util.ArrayList; import java.util.List;import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import …

复现论文ChineseBERT(ONTONOTES数据集)

记录一下自己复现论文《ChineseBERT: Chinese Pretraining Enhanced by Glyph and Pinyin Information》的过程&#xff0c;最近感觉老在调包&#xff0c;一天下来感觉什么也没干&#xff0c;就直播记录一下跑模型的过程吧 事前说明&#xff0c;这是跑项目的实况&#xff0c;如…

实用类详解

第二章 实用类介绍 目录 第二章 实用类介绍 1.枚举 2.包装类及其构造方法 3.Math类 4.Random类 5.String类 总结 内容仅供学习交流&#xff0c;如有问题请留言或私信&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 有空您就点点赞 1.枚举 枚举指由一…

python-注册nacos服务

一、首页 Nacos&#xff08;Naming and Configuration Service&#xff09;是一个用于实现服务注册和发现的开源项目。Nacos注册服务的主要作用是帮助微服务架构中的各个服务实例进行注册和发现&#xff0c;以便于服务之间的通信和协作&#xff0c;另外&#xff0c;也可以在nac…

基于高性能的STM32G031K4T6、STM32G031K6T6、STM32G031K8T6(ARM微控制器)64MHz 闪存 32-LQFP

STM32G0 32位微控制器 (MCU) 适合用于消费、工业和家电领域的应用&#xff0c;并可随时用于物联网 (IoT) 解决方案。这些微控制器具有很高的集成度&#xff0c;基于高性能ARM Cortex-M0 32位RISC内核&#xff0c;工作频率高达64MHz。该器件包含内存保护单元 (MPU)、高速嵌入式内…

大数据赋能交通业务管理——远眺智慧交通集成管控系统

随着交通管理需求的不断提升&#xff0c;原有系统管理模式的缺点逐渐显露&#xff0c;各业务系统的相互独立、各自为战&#xff0c;成为交通管理人员全局把控交通资源、实现交通综合管控的壁垒。 智慧交通集成管控平台通过统一标准&#xff0c;集成交警各类业务系统、整合相关数…

libevent(6)windows上使用iocp网络模型

windows操作系统上不能使用epoll模型&#xff0c;只能使用iocp网络模型。这里我把怎么在windows上使用iocp的代码直接贴上&#xff1a; #include <iostream> #include <signal.h> #include <event2/event.h> #include <event2/listener.h> #include &l…

【Linux从入门到放弃】冯诺依曼体系机构、操作系统及管理的本质

&#x1f9d1;‍&#x1f4bb;作者&#xff1a; 情话0.0 &#x1f4dd;专栏&#xff1a;《Linux从入门到放弃》 &#x1f466;个人简介&#xff1a;一名双非编程菜鸟&#xff0c;在这里分享自己的编程学习笔记&#xff0c;欢迎大家的指正与点赞&#xff0c;谢谢&#xff01; 文…

技术小知识:WAN和LAN区别 ①

1、WAN是外网接接入入口&#xff0c;一般指&#xff1a;外网&#xff0c;广域网&#xff0c;公网。 2、LAN是局域网输出接口&#xff0c;一般指&#xff1a;内网&#xff0c;家庭公司局域网。 局域网是小规模&#xff0c;近距离的一种内部范围网络布局。 外网要跨越的通讯商中…

【GESP】2023年06月图形化一级 -- 去旅行

文章目录 去旅行1. 准备工作2. 功能实现3. 设计思路与实现&#xff08;1&#xff09;角色、舞台背景设置a. 角色设置b. 舞台背景设置 &#xff08;2&#xff09;脚本编写a. 角色&#xff1a;Avery Walking 4. 评分标准 去旅行 1. 准备工作 &#xff08;1&#xff09;删除默认小…

不要错过这所211,专业课简单!保护一志愿,人称电力黄埔军校!

一、学校及专业介绍 华北电力大学&#xff08;North China Electric Power University&#xff09;&#xff0c;简称华电&#xff08;NCEPU&#xff09;&#xff0c;是中华人民共和国教育部直属、由国家电网有限公司等12家特大型电力集团和中国电力企业联合会组成的理事会与教育…

Java容器介绍及其操作方法

一、List ArrayList&#xff0c;LinkedList 特有的函数 <class T> get(int index) 获取下标为index的元素 <class T> set(int index, <class T> element) 改变某个元素 void add(int index, <class T> element) 在下标为index处插入元素…

API信息

API 接口渗透测试

Neo4j的简单使用

1、创建节点 CREATE (:Person {name: Alice, age: 25, city: London}) CREATE (:Person {name: Bob, age: 30, city: New York}) CREATE (:Person {name: Charlie, age: 35, city: Paris})CREATE (:Interest {name: Music}) CREATE (:Interest {name: Sports}) CREATE (:Inter…

Redis实战案例9-封装Redis工具类

1. 封装Redis工具类 方法一和三主要解决缓存穿透的问题&#xff1b; 方法二和四主要解决缓存击穿的问题&#xff1b; 2. 方法一和三 缓存穿透的封装&#xff1b; private final StringRedisTemplate stringRedisTemplate; public CacheClient(StringRedisTemplate stringRedisT…

炫技亮点 Spring Websocket idle check原理

文章目录 原理配置附件Java_websocket空闲检测原理 Spring Websocket 是基于 WebSocket 协议的实现&#xff0c;它提供了一种在客户端和服务器之间实时双向通信的方式。其中&#xff0c;idle check&#xff08;空闲检查&#xff09;是一种机制&#xff0c;用于检测 WebSocket 连…

新项目,不妨采用这种架构分层,很优雅!

大家好&#xff0c;我是飘渺。今天继续更新DDD&微服务的系列文章。 在专栏开篇提到过DDD&#xff08;Domain-Driven Design&#xff0c;领域驱动设计&#xff09;学习起来较为复杂&#xff0c;一方面因为其自身涉及的概念颇多&#xff0c;另一方面&#xff0c;我们往往缺乏…

CF449D Jzzhu and Numbers 题解

题意 给定 A 1 . . . . A n A_1....A_n A1​....An​&#xff0c;选任意个数使得它们异或和为 0 0 0&#xff0c;求方案数。 思路 很朴素的想法是枚举每个数&#xff0c;然后进行 0-1 背包方案数统计&#xff0c;时间复杂度 O ( n n ) O(n \times n) O(nn)。 而根据前面几…

linux——解压和压缩

目录 1.压缩格式 2. tar命令 3.tar命令压缩 4. tar解压 5. zip命令压缩文件 6. unzip 命令解压 7. 总结 1.压缩格式 2. tar命令 3.tar命令压缩 4. tar解压 5. zip命令压缩文件 6. unzip 命令解压 7. 总结