【算法】【优选算法】多源BFS

【算法】【优选算法】多源BFS

目录

一、多源BFS

单源最短路:只有一个起点到终点的最短路问题。
多源最短路问题:有多个起点到终点的最短路问题。
多源BFS:用BFS来解决边权相同的多源最短路问题。

解法:

  1. 暴力解题,把多源最短路问题,转化为若干个单源最短路问题。
  2. 把所有起点当成一个起点,问题就变成了单源最短路问题。

二、542.01 矩阵

题目链接:542.01 矩阵

题目描述:

题目解析:

  • 给一个只有0 1 的二维数组,计算其中每一个元素到0的最短距离,自己是0距离就是0,将距离存入一个相同规模二维数组的下标中。

法一:
解题思路:

  • 我们如果遍历数组,找到1后,就进行求该元素到0的最短距离。
  • 求最短距离就使用前面求权值相同的最短距离的方法即可。
  • 但是会超时。
    解题代码:
//时间复杂度:O(M*N*M*N)//空间复杂度:O(M*N)classSolution{int[] dx ={0,0,1,-1};int[] dy ={1,-1,0,0};int m,n;publicint[][]updateMatrix(int[][] mat){ m = mat.length; n = mat[0].length;int[][] ret =newint[m][n];//遍历mat,遍历到1找最近的0,for(int i =0; i < m; i++){for(int j =0; j < n; j++){if(0== mat[i][j]){ ret[i][j]=0;}else{ ret[i][j]=bfs(mat,i,j);}}}return ret;}publicintbfs(int[][] mat,int x,int y){Queue<int[]> queue =newLinkedList<>(); queue.add(newint[]{x,y});boolean[][] flag =newboolean[m][n]; flag[x][y]=true;int ret =0;while(!queue.isEmpty()){ ret++;int size = queue.size();while(size--!=0){int[] arr = queue.poll();for(int i =0; i <4; i++){int a = arr[0]+ dx[i];int b = arr[1]+ dy[i];//入队if(a >=0&& a < m && b >=0&& b < n &&!flag[a][b]&& mat[a][b]!=0){ flag[a][b]=true; queue.add(newint[]{a,b});}//结束条件if(a >=0&& a < m && b >=0&& b < n && mat[a][b]==0)return ret;}}}return0;}}

法二:
解题思路:

  • 我们先将数组中的所有0下标,记录下来放入队列中。
  • 然后我们层序遍历,每一次循环遍历完当前队列中的值,往外BFS寻找没被标记的1,寻找的循环次数就是这个1的最短距离。

解题代码:

//时间复杂度:O(M*N)//空间复杂度:O(M*N)classSolution{int[] dx ={0,0,1,-1};int[] dy ={1,-1,0,0};publicint[][]updateMatrix(int[][] mat){int m = mat.length;int n = mat[0].length;int[][] ret =newint[m][n];boolean[][] flag =newboolean[m][n];Queue<int[]> queue =newLinkedList<>();//遍历mat,先把0全部放入队列,for(int i =0; i < m; i++){for(int j =0; j < n; j++){if(0== mat[i][j]){ queue.add(newint[]{i,j}); ret[i][j]=0;}}}//遍历队列中的元素,层序递进int tep =0;while(!queue.isEmpty()){ tep++;int size = queue.size();while(size--!=0){int[] arr = queue.poll();for(int i =0; i <4; i++){int x = arr[0]+ dx[i];int y = arr[1]+ dy[i];//入队if( x >=0&& x < m && y >=0&& y < n &&1== mat[x][y]&&!flag[x][y]){ flag[x][y]=true; queue.add(newint[]{x,y}); ret[x][y]= tep;}}}}return ret;}}

改进:

  • 我们就可以直接使用结果数组,来达到上面的flag数组的记录功能,也不再需要记录层数。
  • 我们将mat数组中元素为1对应的ret结果数组元素记录为-1;
  • 当我们BFS的时候,如果ret元素为-1,那么证明没有记录过,那么这个元素值就是出队列元素对应的ret值加一;
  • 当ret中数组元素没有-1了就会结束。
//时间复杂度:O(M*N)//空间复杂度:O(M*N)classSolution{int[] dx =newint[]{0,0,1,-1};int[] dy =newint[]{1,-1,0,0};publicint[][]updateMatrix(int[][] mat){int m = mat.length;int n = mat[0].length;int[][] ret =newint[m][n];Queue<int[]> queue =newLinkedList<>();for(int i =0; i < m; i++){for(int j =0; j < n; j++){ ret[i][j]=-1;if(mat[i][j]==0){ queue.add(newint[]{i,j}); ret[i][j]=0;}}}while(!queue.isEmpty()){int[] arr = queue.poll();for(int i =0; i <4; i++){int x = arr[0]+ dx[i];int y = arr[1]+ dy[i];//入队if(x >=0&& x < m && y >=0&& y < n && ret[x][y]==-1){ queue.add(newint[]{x,y}); ret[x][y]= ret[arr[0]][arr[1]]+1;}}}return ret;}}

三、1020.⻜地的数量

题目链接:1020.⻜地的数量

题目描述:

题目解析:

  • 给我们一个只有0 1 的数组,让我们统计(上下左右)连成块的1,并且这其中1没有在数组边的个数。

解题思路:

  • 我们使用标记数组标记 0 和 处于边上的 1
  • 将边上 1 的坐标放入队列
  • 在对队列中的元素实行BFS,入队条件是没被标记的元素。
  • 执行完BFS,就只会有符合条件的1 没有被标记。
  • 我们使用数组元素个数作为返回值,标记一次返回值就减1,最后就是所求值。

解题代码:

//时间复杂度:O(M*N)//空间复杂度:O(M*N)classSolution{int[] dx =newint[]{0,0,1,-1};int[] dy =newint[]{1,-1,0,0};publicintnumEnclaves(int[][] grid){int m = grid.length;int n = grid[0].length;Queue<int[]> queue =newLinkedList<>();boolean[][] flag =newboolean[m][n];//先将所有边界1入队并标记,将0标记int ret = m * n;for(int i =0; i < m; i++){for(int j =0; j < n; j++){if(grid[i][j]==1&&(i ==0|| i == m -1|| j ==0|| j == n -1)){ queue.add(newint[]{i,j}); flag[i][j]=true; ret--;}elseif(grid[i][j]==0){ flag[i][j]=true; ret--;}}}while(!queue.isEmpty()){int[] arr = queue.poll();for(int i =0; i <4; i++){int x = arr[0]+ dx[i];int y = arr[1]+ dy[i];if(x >=0&& x < m && y >=0&& y < n &&!flag[x][y]){ ret--; queue.add(newint[]{x,y}); flag[x][y]=true;}}}return ret;}}

四、1765. 地图中的最⾼点

题目链接:1765. 地图中的最⾼点

题目描述:

题目解析:

  • 给我们一个数组,其中1代表水域,0代表陆地。
  • 要求返回一个height高度数组,数组元素比上下左右元素高度差不超过一。返回能使height元素达到最大值的结果。

解题思路:

  • 我们先将水域入队,然后从水域元素往外扩,将四周元素入队,并且使其height值比让他入队的元素大1
  • 每个元素只能入一次对列,我们要有标技数组,我们可以直接使用height为标记数组,初始化时将陆地元素赋值-1,只要是值不是-1的元素就证明入过对列了。

解题代码:

//时间复杂度:O(M*N)//空间复杂度:O(M*N)classSolution{int[] dx =newint[]{0,0,1,-1};int[] dy =newint[]{1,-1,0,0};publicint[][]highestPeak(int[][] isWater){int m = isWater.length;int n = isWater[0].length;int[][] height =newint[m][n];Queue<int[]> queue =newLinkedList<>();//遍历isWater数组,1水域对应height为0,并入对,其余为-1,起标记作用for(int i =0; i < m; i++){for(int j =0; j < n; j++){ height[i][j]=-1;if(isWater[i][j]==1){ height[i][j]=0; queue.add(newint[]{i,j});}}}//BFSwhile(!queue.isEmpty()){int[] arr = queue.poll();for(int i =0; i <4; i++){int x = arr[0]+ dx[i];int y = arr[1]+ dy[i];//height为-1入队,值为出队列元素加1if(x >=0&& x < m && y >=0&& y < n && height[x][y]==-1){ height[x][y]= height[arr[0]][arr[1]]+1; queue.add(newint[]{x,y});}}}return height;}}

五、1162. 地图分析

题目链接:1162. 地图分析

题目描述:

题目解析:

  • 给我们一个grid数组,只有0 1
  • 找出 0 通过上下左右走到最近的1 的距离的最大值

解题思路:

  • 一个最经典的多源BFS
  • 我们从1开始走(将1全部入队),每走一次(将当前队列元素出完,将元素上下左右的没标记过的0入队),直到走完所有元素,最后走的次数就是结果。
    解题代码:
//时间复杂度:O(M*N)//空间复杂度:O(M*N)classSolution{int[] dx =newint[]{0,0,1,-1};int[] dy =newint[]{1,-1,0,0};publicintmaxDistance(int[][] grid){int m = grid.length;int n = grid[0].length;int ret =0;Queue<int[]> queue =newLinkedList<>();boolean[][] flag =newboolean[m][n];//所有1入队for(int i =0; i < m; i++){for(int j =0; j < n; j++){if(grid[i][j]==1){ queue.add(newint[]{i,j}); flag[i][j]=true; ret++;}}}//判断是否全0或全1if(ret ==0|| ret == m * n)return-1; ret =-1;//BFSwhile(!queue.isEmpty()){int size = queue.size(); ret++;while(size--!=0){int[] arr = queue.poll();for(int i =0; i <4; i++){int x = arr[0]+ dx[i];int y = arr[1]+ dy[i];//入队if(x >=0&& x < m && y >=0&& y < n &&!flag[x][y]&& grid[x][y]==0){ queue.add(newint[]{x,y}); flag[x][y]=true;}}}}return ret;}}

Read more

Effective Modern C++ 条款37:使std::thread在所有路径最后都不可结合

Effective Modern C++ 条款37:使std::thread在所有路径最后都不可结合

Effective Modern C++ 条款37:使std::thread在所有路径最后都不可结合 * 引言:线程生命周期的关键问题 * 线程的两种状态:可结合与不可结合 * 可结合(Joinable)状态的特征 * 不可结合(Unjoinable)状态的四种情况 * 为什么可结合性如此重要? * 两种被拒绝的替代方案 * RAII拯救方案:ThreadRAII类 * ThreadRAII实现详解 * 关键设计决策 * 实际应用案例 * 高级讨论:何时选择join或detach * 性能考量与最佳实践 * 结论:让线程管理无忧 BiliBili上对应的视频为:https://www.bilibili.com/video/BV1iZZgBiE9j 引言:线程生命周期的关键问题 在多线程程序设计中,std::thread的管理是一个看似简单实则暗藏玄机的话题。想象一下,你精心设计的并发程序在大多数情况下运行良好,却在某些边缘情况下突然崩溃——这正是许多开发者在使用原生线程时遇到的噩梦场景。本文将深入探讨std::thread对象

By Ne0inhk
RabbitMQ如何成为分布式系统的“神经中枢“?——从安装部署到C++调用实战的完整流程,带你体验它的奥妙所在!​

RabbitMQ如何成为分布式系统的“神经中枢“?——从安装部署到C++调用实战的完整流程,带你体验它的奥妙所在!​

文章目录 * 本篇摘要 * ①·RabbitMq(轻量级消息队列中间件) 介绍 * RabbitMQ 是什么? * 核心功能与特点 * 1. **核心功能** * 2. **核心优势** * RabbitMQ 的核心概念 * 1. **生产者(Producer)** * 2. **消费者(Consumer)** * 3. **队列(Queue)** * 4. **交换机(Exchange)** * 5. **绑定(Binding)** * 工作流程(以 Direct 交换机为例) * 常见应用场景 * RabbitMQ 与相关技术对比 * 图像理解 * 总结一句话 * ②·RabbitMq 安装教程 * RabbitMq安装 * **1. 安装 RabbitMQ** * **2. 启动 & 检查状态** * **3. 创建管理员用户(

By Ne0inhk
【STL】手撕 vector:从 0 到 1 模拟实现 STL 容器

【STL】手撕 vector:从 0 到 1 模拟实现 STL 容器

前言 STL 容器是 C++ 开发中绕不开的 “神兵利器”,而vector作为最常用的动态数组容器,更是新手入门 STL 的核心内容。但多数时候,我们只是 “会用”vector,却对它的底层逻辑一知半解 —— 比如它如何动态扩容?push_back的内存管理是怎样的?构造函数的匹配规则为何如此复杂? 与其停留在 “黑盒调用” 的层面,不如亲手模拟实现一个 vector:从底层的指针管理(_start/_finish/_endofstorage),到核心接口(push_back/resize/operator[]),再到构造、拷贝等特殊函数的实现,一步步揭开 STL 容器的面纱。 本文不会纠结过于晦涩的标准细节,而是以 “实用、易懂” 为核心,带你用 C++ 手动实现一个具备基础功能的vector—— 既能加深对容器原理的理解,也能锻炼 C++ 的底层编程能力。

By Ne0inhk
手把手实现 STL Set/Map:从零编写一棵红黑树到完整容器封装

手把手实现 STL Set/Map:从零编写一棵红黑树到完整容器封装

🔥草莓熊Lotso:个人主页 ❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受! 🎬 博主简介: 文章目录 * 前言: * 一. 架构与实现:总览设计框架,深入源码细节 * 二. 核心设计思路:红黑树的泛型复用 * 2.1 红黑树的模板参数设计 * 2.2 仿函数 KeyOfT:统一 key 提取逻辑 * 2.3 核心约束:key 不可修改 * 三. 基础组件实现:红黑树与仿函数 * 3.1 红黑树节点结构 * 3.2 仿函数实现(map/set 层) * 3.2.1

By Ne0inhk