算法.图论-BFS及其拓展

文章目录

    • 广度优先搜索简介
    • 经典bfs习题
      • 地图分析
      • 贴纸拼词
    • 01bfs解析
      • 基本过程
      • 相关习题

广度优先搜索简介

  1. bfs的特点是逐层扩散, 从源头到目标点扩散了几层, 最短路就是多少

  2. bfs的使用特征是任意两个节点的距离(权值)是相同的(无向图, 矩阵天然满足这一特点)

  3. bfs开始的时候可以是单个源头, 也可以多个源头(单源bfs, 和多源bfs)

  4. bfs进出队列的时候可以是单点弹出, 也可以是整层弹出

    如果是单点弹出的时候, 队列中存放的是当前的节点和距离源点的距离

    整层弹出则不需要, 只需要保留一个level计数就可以知道到源点的距离

  5. bfs进行时通常需要一个visit数组(一般是boolean[][])来标记已经遍历到的位置

  6. bfs的时候一个点向四个方向遍历的时候通常可以用一个move数组搞定(下面是举例)

    //建立一个全局的move数组来进行四个方向的遍历(上, 右, 下, 左)
    private static final int[] move = new int[]{-1, 0, 1, 0, -1};
    //假设下面的函数是用来进行 (i, j) 的遍历的
    private static void traversal(int i, int j, int[][] matrix, boolean[][] visit){
        //不用写四个if, 仅仅需要进行for循环四次就可以了
        int r = matrix.length;
        int c = matrix[0].length;
        for(int k = 0; k < 4; k++){
            int ni = i + move[k];
            int nj = j + move[k + 1];
            if(ni >= 0 && ni < r && nj >= 0 && nj < c && !visit[ni][nj]){
                //下一个位置不越界并且没有访问过
                //.....进行处理逻辑, 并最终把visit数组的这一个位置置为true
                visit[ni][nj] = true;
            }
        }
    }
    
  7. bfs设计的时候有很多的剪枝的操作需要进行一定的摸索

经典bfs习题

地图分析

链接: leetcode1162.地图分析

题目简介:
在这里插入图片描述

解释一下什么是曼哈顿距离, 就是一个点到另外一个点的横坐标的差值和纵坐标的差值之和, 这与我们习惯认为的对角线距离区别开

这种距离的定义通常用于矩形的表格之中(实质上: bfs最广的应用就是矩形格之中, 因为这是一种天然的无向图)

这道题本质上是要找距离陆地最近的海洋的最远的距离, 翻译成人话就是寻找距离陆地最远的海洋, 那我们直接以陆地为源点开始进行bfs即可

我们下面给出来两种实现的方案

第一种是单点弹出的方法

    //创建一个move数组
    private static final int[] move = new int[]{-1, 0, 1, 0, -1};

    //创建一个全局的visit数组
    private static final int MAXM = 101;

    private static final boolean[][] visit = new boolean[MAXM][MAXM];

    //方法一: 单点弹出的方式
    public int maxDistance(int[][] grid) {
        int r = grid.length;
        int c = grid[0].length;
        int seas = 0;
        Queue<int[]> q = new ArrayDeque<>();
        //遍历一下grid数组初始化队列元素同时初始化visit数组
        for(int i = 0; i < r; i++){
            for(int j = 0; j < c; j++){
                if(grid[i][j] == 1){
                    visit[i][j] = true;
                    q.offer(new int[]{i, j, 0});
                }else{
                    visit[i][j] = false;
                    seas++;
                }
            }
        }
        //特殊条件直接返回
        if(seas == r * c || seas == 0){
            return -1;
        }
        //进行bfs的主流程
        int distanse = 1;
        while(!q.isEmpty()){
            int[] cur = q.poll();
            //向四个方向尝试扩展
            for(int k = 0; k < 4; k++){
                int nx = cur[0] + move[k];
                int ny = cur[1] + move[k + 1];
                if(nx >= 0 && nx < r && ny >= 0 && ny < c && !visit[nx][ny]){
                    visit[nx][ny] = true;
                    q.offer(new int[]{nx, ny, cur[2] + 1});
                    distanse = Math.max(distanse, cur[2] + 1);
                }
            }
        }
        return distanse;
    }
}

第二种就是整层弹出的方法

class Solution {
    //创建一个move数组
    private static final int[] move = new int[]{-1, 0, 1, 0, -1};

    //创建一个全局的visit数组
    private static final int MAXM = 101;

    private static final boolean[][] visit = new boolean[MAXM][MAXM];

    //方法二 : 整层弹出的方式
    public int maxDistance(int[][] grid) {
        int r = grid.length;
        int c = grid[0].length;
        int seas = 0;
        Queue<int[]> q = new ArrayDeque<>();
        //遍历一下grid数组初始化队列元素同时初始化visit数组
        for(int i = 0; i < r; i++){
            for(int j = 0; j < c; j++){
                if(grid[i][j] == 1){
                    visit[i][j] = true;
                    q.offer(new int[]{i, j});
                }else{
                    visit[i][j] = false;
                    seas++;
                }
            }
        }
        //特殊条件直接返回
        if(seas == r * c || seas == 0){
            return -1;
        }
        //进行bfs的主流程
        int level = 0;
        while(!q.isEmpty()){
            level++;
            int sz = q.size();
            while(sz-- != 0){
                int[] cur = q.poll();
                //尝试向四个方向扩展
                for(int k = 0; k < 4; k++){
                    int nx = cur[0] + move[k];
                    int ny = cur[1] + move[k + 1];
                    if(nx >= 0 && nx < r && ny >= 0 && ny < c && !visit[nx][ny]){
                        q.offer(new int[]{nx, ny});
                        visit[nx][ny] = true;
                    }
                }
            }
        }
        return level - 1;
    }
}

贴纸拼词

链接: [leetcode691.贴纸拼词](. - 力扣(LeetCode))

题目描述: 在这里插入图片描述

这个题的解题思路就是, 对于目标字符串target, 我们想要使用最少的代价进行拼词,

这道题如何想到用bfs主要就是对于一个字符串

target, 我们提供的每一个词都有对应的一种展开, 如下图

图片:
在这里插入图片描述

从上面的演示过程也不难看出, 我们这个本题剪枝的关键就是对target的进行排序操作, 主要就是优先削减头部的字符

代码实现如下(重点在理解逻辑)

class Solution {
    public static int minStickers(String[] stickers, String target) {
        //首先对数组中的单词排序并进行词频统计
        List<int[]> times = new ArrayList<>();
        for(int i = 0; i < stickers.length; i++){
            int[] temp = new int[26];
            String changeStr = sort(stickers[i], temp);
            stickers[i] = changeStr;
            times.add(temp);
        }
        //排序一下target字符串
        int[] targetTime = new int[26];
        target = sort(target, targetTime);
        Queue<String> q = new ArrayDeque<>();
        HashSet<String> set = new HashSet<>();
        StringBuilder sp = new StringBuilder();
        //进行bfs的主流程
        q.offer(target);
        int level = 0;
        //本质上还是我们弹出的逻辑没有搞懂, 我们应该一层一层的弹出
        while(!q.isEmpty()){
            int sz = q.size();
            level++;
            while(sz-- != 0){
                int[] curTime = new int[26];
                String cur = q.poll();
                //统计一下当前的词频
                for(int i = 0; i < cur.length(); i++){
                    curTime[cur.charAt(i) - 'a']++;
                }
                for(int i = 0; i < stickers.length; i++){
                    if(times.get(i)[cur.charAt(0) - 'a'] != 0){
                        String next = buildStr(curTime, times.get(i), sp);
                        if(next.equals("")) return level;
                        if(!set.contains(next)) {
                            set.add(next);
                            q.offer(next);
                        }
                    }
                }
            }
        }
        return -1;
    }

    //对字符串排序的方法, 顺便统计一下词频
    private static String sort(String s, int[] temp){
        char[] cs = s.toCharArray();
        for(char elem : cs){
            temp[elem - 'a']++;
        }
        Arrays.sort(cs);
        return String.valueOf(cs);
    }

    //生成一个新的字符串
    private static String buildStr(int[] curTime, int[] time, StringBuilder sp){
        sp.setLength(0);
        for(int i = 0; i < 26; i++){
            if(curTime[i] != 0){
                for(int j = 0; j < Math.max(curTime[i] - time[i], 0); j++){
                    sp.append((char)(i + 'a'));
                }
            }
        }
        return sp.toString();
    }

}

01bfs解析

基本过程

01bfs是一种特殊的bfs, 适用于01图找寻最短路径的情况, 01bfs时间复杂度是O(节点数量 + 边的数量) 下图是我们的实例

图片:
在这里插入图片描述

上面就是一个01bfs找寻最短路径的情况, 我们的解题的流程是固定的, 如下(正确性证明略), 主要就是双端队列结合bfs

  1. 创建一个distance表, 含义就是源点到i点的最短距离是多少

    大小就是所有的节点位置, 初始化所有点的distance[i] = Integer.MAX_VALUE

  2. 将源点加入双端队列, 并修改distance[源点] = 0

  3. 当队列不为空的时候进入循环(下面就是伪代码)

    while(!queue.isEmpty()){
        //弹出一个节点(弹出的时候一定从头部弹出)
        Node node = queue.poll();
        //如果这个位置就是要找的目标节点就直接返回
        if(node == targetNode) return distance[node];
        //找到这个节点去的下一个位置(可能有多个...)
        int next = node -> next;
        //weight就是这两个点之间的权值(0 or 1)
        int weight = 0 or 1;    
        if(distance[node] + weight < distance[next]){
            //此时说明到达next的位置可以边的更小就更新
            distance[next] = distance[node] + weight;
            //然后在队列中加入这个位置, 如果刚才的权值weight == 0, 就从头部加入, 如果是1就从尾部加入
            if(weight == 0){
                queue.offerFirst(node);
            }else{
                queue.offerLast(node);
            }
        }
    }
    

相关习题

图片: 在这里插入图片描述

链接: leetcode2290.到达角落的最小代价

其实就是01bfs的模板题

class Solution {
    //经典01dfs板子题
    private static final int[] move = new int[]{-1, 0, 1, 0, -1};

    public int minimumObstacles(int[][] grid) {
        int r = grid.length;
        int c = grid[0].length;
        //初始化一个distance数组
        int[][] distance = new int[r][c];
        for(int i = 0; i < r; i++){
            for(int j = 0; j < c; j++){
                distance[i][j] = Integer.MAX_VALUE;
            }
        }
        //创建一个双端队列
        Deque<int[]> dq = new ArrayDeque<>();
        dq.offer(new int[]{0, 0});
        distance[0][0] = 0;
        while(!dq.isEmpty()){
            int[] cur = dq.poll();
            //如果是目标节点
            if(cur[0] == r - 1 && cur[1] == c - 1) return distance[cur[0]][cur[1]];
            //尝试向四个方向扩展
            for(int k = 0; k < 4; k++){
                int nx = cur[0] + move[k];
                int ny = cur[1] + move[k + 1];
                if(nx >= 0 && nx < r && ny >= 0 && ny < c && distance[cur[0]][cur[1]] + grid[nx][ny] < distance[nx][ny]){
                    distance[nx][ny] = distance[cur[0]][cur[1]] + grid[nx][ny];
                    if(grid[nx][ny] == 0){
                        dq.offerFirst(new int[]{nx, ny});
                    }else{
                        dq.offerLast(new int[]{nx, ny});
                    }
                }
            }
        }
        return -1;
    }
rst(new int[]{nx, ny});
                    }else{
                        dq.offerLast(new int[]{nx, ny});
                    }
                }
            }
        }
        return -1;
    }
}

链接: leetcode1368.箭头数组的最短代价

图片: 在这里插入图片描述

class Solution {
    //这个move数组的设计是比较的精巧的
    private static final int[][] move = {{0}, {0, 1}, {0, -1}, {1, 0}, {-1, 0}};

    public int minCost(int[][] grid) {
        int r = grid.length;
        int c = grid[0].length;
        //初始化distance数组
        int[][] distance = new int[r][c];
        for(int i = 0; i < r; i++){
            Arrays.fill(distance[i], Integer.MAX_VALUE);
        }
        //创建双端队列
        Deque<int[]> dq = new ArrayDeque<>();
        dq.offer(new int[]{0, 0});
        distance[0][0] = 0;
        while(!dq.isEmpty()){
            int[] cur = dq.poll();
            int x = cur[0];
            int y = cur[1];
            if(x == r - 1 && y == c - 1) return distance[x][y];
            for(int i = 1; i < 5; i++){
                int nx = x + move[i][0];
                int ny = y + move[i][1];
                int weight = i == grid[x][y] ? 0 : 1;
                if(nx >= 0 && nx < r && ny >= 0 && ny < c && distance[x][y] + weight < distance[nx][ny]){
                    distance[nx][ny] = distance[x][y] + weight;
                    if(weight == 0){
                        dq.offerFirst(new int[]{nx, ny});
                    }else{
                        dq.offerLast(new int[]{nx, ny});
                    }
                }
            }
        }
        return -1;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

树莓派应用--AI项目实战篇来啦-10.OpenCV进行车牌检测

1. 介绍 本项目使用 esseract、OpenCV和Python探索光学字符识别&#xff08;OCR&#xff09;的神奇世界&#xff0c;本项目将 带你了解最受欢迎的OCR引擎 Tesseract 背后的技术&#xff0c;以及如何用 Pytesseract 和 OpenCV实现字符识别。 从图像中检测字符的技术称为…

图(Java语言实现)

一、图的概念 顶点&#xff08;Vertex&#xff09;&#xff1a;图中的数据元素&#xff0c;我们称之为顶点&#xff0c;图至少有一个顶点&#xff08;非空有穷集合&#xff09;。 边&#xff08;Edge&#xff09;&#xff1a;顶点之间的关系用边表示。 1.图&#xff08;Graph…

Python Django 数据库优化与性能调优

Python Django 数据库优化与性能调优 Django 是一个非常流行的 Python Web 框架&#xff0c;它的 ORM&#xff08;对象关系映射&#xff09;允许开发者以简单且直观的方式操作数据库。然而&#xff0c;随着数据量的增长&#xff0c;数据库操作的效率可能会成为瓶颈&#xff0c…

如何在Ubuntu上更改MySQL数据存储路径

文章目录 0 背景1 备份现有数据库数据2 停止 MySQL 服务3 复制现有的 MySQL 数据到新目录4 修改 MySQL 配置文件5 更新 AppArmor 或 SELinux 配置&#xff08;如有启用&#xff09;6. 修改 MySQL 系统文件中的 datadir7. 启动 MySQL 服务8. 验证更改参考资料 0 背景 在原先划分…

股市入门常见术语介绍

鉴于最近行情讨论火热&#xff0c;我也想借此平台&#xff0c;结合我大学时期身边同学老师的投资经历&#xff0c;写一篇交易入门术语简介。内容不多但是足以达到科普之用。 ​ 希望大家能谨慎对待投资&#xff0c;始终保持谦虚学习的态度。不要迷失在瞬息万变的金融市场&…

webstorm 编辑器配置及配置迁移

1.下载地址 WebStorm&#xff1a;JetBrains 出品的 JavaScript 和 TypeScript IDE 其他版本下载地址 2.安装 点击下一步安装&#xff0c;可根据需要是否删除已有版本 注意&#xff1a; 完成安装后需要激活 3.设置快捷键 以下为个人常用可跳过或根据需要设置 如&#xff1a…

满级抗摔续航王者,荣耀X60系列发布,起步价仅1199元

10月16日&#xff0c;荣耀X60系列暨荣耀平板新品发布会正式举办&#xff0c;荣耀X60 Pro、荣耀X60以及荣耀平板GT Pro、荣耀亲选耳机LCHSE X7e、荣耀亲选WhizKid儿童手表2 Pro等新品悉数亮相。其中&#xff0c;荣耀X60 Pro首次搭载6600mAh最大青海湖电池、绿洲护眼屏、双向北斗…

pta-7-6 学生类设计

题目要求&#xff1a; 设计一个类Student&#xff0c;并在Main类中生成Student类对象进行测试 1.对于Student类&#xff0c;设计私有属性name和age&#xff0c;并为每一个成员变量name和age设计其setXXX&#xff08;&#xff09;和getXXX&#xff08;&#xff09;方法,并对于s…

GPT-SOVIT模型部署指南

一、模型介绍 强大的小样本语音转换和文本转语音 WebUI。 具有以下特征&#xff1a; 零样本 TTS&#xff1a; 输入 5 秒的声音样本并体验即时文本到语音的转换。少量样本 TTS&#xff1a; 仅使用 1 分钟的训练数据对模型进行微调&#xff0c;以提高语音相似度和真实感。跨语…

【Oracle数据库进阶】001.SQL基础查询_查询语句

课 程 推 荐我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448;入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448;虚 拟 环 境 搭 建 &#xff1a;&#x1…

2023年五一杯数学建模C题双碳目标下低碳建筑研究求解全过程论文及程序

2023年五一杯数学建模 C题 双碳目标下低碳建筑研究 原题再现&#xff1a; “双碳”即碳达峰与碳中和的简称&#xff0c;我国力争2030年前实现碳达峰&#xff0c;2060年前实现碳中和。“双碳”战略倡导绿色、环保、低碳的生活方式。我国加快降低碳排放步伐&#xff0c;大力推进…

AUTOSAR_EXP_ARAComAPI的5章笔记(13)

☞返回总目录 5.4.7 事件&#xff08;Events&#xff09; 在骨架侧&#xff0c;服务实现负责通知事件的发生。如 5.4.2 RadarService Skeleton Class 所示&#xff0c;骨架为每个事件提供一个事件包装类的成员。骨架的事件包装类与代理的事件包装类看起来明显不同。 在骨架端…

[已解决]DockerTarBuilder永久解决镜像docker拉取异常问题

前阵子发现阿里云的docker加速镜像失效了&#xff08;甚至连nginx都拉取不了&#xff09;&#xff0c;重新换了并且加多了网络上比较常用的dokcer加速源&#xff0c;可以解决一部分问题&#xff0c;但仍然有一些镜像的某个版本或一些比较冷的镜像就是拉取不了&#xff0c;原因未…

libaom 源码分析:aomdec.c 文件

aomdec.c 功能:libaom 项目完成视频解码过程的 demo文件位置:libaom/apps/aomdec.c函数关系 命令行说明 终端输入 ./aomdec --help,输出如下,展示如何使用方法。Usage: ./aomdec <options> filenameOptions:--help Show usage options and exit…

基于Springboot+Vue的小型民营加油站管理系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…

libaom 源码分析综述【持续更新】

libaom libaom 是 AOMedia&#xff08;开放媒体联盟&#xff09;开发的一个开源视频编解码器库&#xff0c;它是 AV1 视频压缩格式的参考实现&#xff0c;并被广泛用于多种生产系统中。libaom 支持多种功能&#xff0c;包括可扩展视频编码&#xff08;SVC&#xff09;、实时通信…

Linux权限和开发工具(1)

文章目录 1.Linux根目录的相关文件夹2.Linux软件管理器yum3.Linux编辑器-vim的基础使用1.命令模式下一些命令:有关光标的操作:有关复制删除的操作:有关字符替换的相关操作:有关注释的相关操作: 2.插入模式3.底行模式下一些命令:实现双窗口 4.vim命令 4.vim配置5.Linux编译器-gc…

架构设计笔记-9-软件可靠性

目录 知识要点 综合知识 案例分析 1.可靠性特性&#xff0c;软硬件可靠性对比 论文 1.论软件可靠性设计技术的应用 知识要点 软件架构需求过程主要是获取用户需求&#xff0c;标识系统中所要用到的构件&#xff0c;并进行架构需求评审。其中&#xff0c;标识构件又详细地…

AI周报(10.6-10.12)

AI应用-AI中医诊疗 AI中医诊疗通过整合中医“望、闻、问、切”的传统诊断方法&#xff0c;并结合现代AI技术&#xff0c;如自然语言处理和图像识别&#xff0c;来辅助医生进行更精准的诊断。 望诊&#xff0c;作为中医四诊之首&#xff0c;其精髓在于“司外揣内”。医者通过细致…