【算法深练】BFS:“由近及远”的遍历艺术,广度优先算法题型全解析

前言

宽度优先遍历BFS与深度优先遍历DFS有本质上的区别,DFS是一直扩到低之后找返回,而BFS是一层层的扩展就像剥洋葱皮一样。

  • 通常BFS是将所有路径同时进行尝试,所以BFS找到的第一个满足条件的位置,一定是路径最短的位置,而DFS是将一条路走到底,走完了还不能确定是否是最短的;所以在解决求最短路是BFS效率更高。
  • 因为DFS是一条路走到底,所以DFS得到得到一条支路的速度比BFS更快,所以DFS通常可以用来检查某个特定的支路是否存在。

下面通过一个题看看BFS的样子:

此题就是经典的BFS题目,给定一个位置,找出要走多少步才能找到出口:因为是以人为坐标位置一层层的向外进行扩展的,所以可以采用宽度优先遍历,一层层的向外进行扩展知道扩展到出口位置。

深度优先遍历解题步骤:

  1.  确定起始位置+准备工作(设置队列存放下一步可以走的位置);
  2.  找下一个位置,将下一个位置入队列,对已经入队列的位置进行标记;
  3.  判断下一个位置是否满足条件;
  4.  进行下一层循环,继续2,3步.

 PS:本篇博客中的所有题目均来自于灵茶山艾府 - 力扣(LeetCode)分享的题单

BFS经典题目

1926. 迷宫中离入口最近的出口

就是前言的题目,此处直接贴代码:

class Solution {
public:int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {//使用BFS一层层的向外扩int n=maze.size(),m=maze[0].size(),step=0;int dx[]={0,0,1,-1};int dy[]={1,-1,0,0};queue<pair<int,int>> st;  //记录向外扩的坐标st.push({entrance[0],entrance[1]});maze[entrance[0]][entrance[1]]='+';while(!st.empty()){//此时st不为空,说明还可以像外扩int num=st.size();step++;     //先外扩,次数+1for(int i=0;i<num;i++){int x=st.front().first,y=st.front().second;st.pop();for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k];if(a<0||a>=n||b<0||b>=m||maze[a][b]=='+') continue;  //不满足条件的坐标if(a==0||a==n-1||b==0||b==m-1) return step;     //找到了目标位置maze[a][b]='+';   //在此处就需要将矩阵坐标置为'+'防止其他位置重复加入,导致超时st.push({a,b});   //将当前位置加入到队列中}}   }return -1;}
};

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

此题如果使用DFS会导致已经走过的路径不好处理,如果直接将已经走过的路径禁止掉,就会导致一些更短的路径被裁剪掉,如果不进行裁剪直接遍历全部会超时。所以此题使用BFS会更方便处理一些。

class Solution {
public:int shortestPathBinaryMatrix(vector<vector<int>>& grid) {if(grid[0][0]==1) return -1;int n=grid.size(),m=grid[0].size();int dx[]={0,0,-1,-1,-1,1,1,1};int dy[]={-1,1,1,-1,0,1,-1,0};//此题如果使用DFS会导致已经走过的路径不好处理,如果直接将已经走过的路径禁止掉,就会导致一些更短的路径被裁剪掉//所以此题使用BFS更好queue<pair<int,int>> q;vector<vector<int>> vist(n,vector<int>(m));q.push({0,0});int ret=0;while(!q.empty()){ret++;int _size=q.size();for(int i=0;i<_size;i++){int x=q.front().first,y=q.front().second;vist[x][y]=1;q.pop();if(x==n-1&&y==m-1) return ret;for(int k=0;k<8;k++){int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]==0&&!vist[a][b]) {q.push({a,b});vist[a][b]=1;}}}}return -1;}
};

1162. 地图分析

与上一题一样,只不过有多个起始位置,需要从多个起始位置出发,找最大的距离。可以枚举每一个位置,在每一个0的位置找最近的路径,再找出路径中的最大值。

class Solution {
public:int maxDistance(vector<vector<int>>& grid) {int n=grid.size(),m=grid[0].size();int dx[]={0,0,-1,1};int dy[]={-1,1,0,0};function<int(int,int)> bfs=[&](int x1,int y1)  //从(x1,y1)开始向周围找最近距离{vector<vector<int>> vist(n,vector<int>(m));int step=0;queue<pair<int,int>> q;q.push({x1,y1});vist[x1][y1]=1;while(!q.empty()){step++;int _size=q.size();for(int z=0;z<_size;z++){int x=q.front().first,y=q.front().second;q.pop();for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]) return step;if(a>=0&&a<n&&b>=0&&b<m&&!grid[a][b]&&!vist[a][b])  {q.push({a,b});vist[a][b]=1;}}}}return -1;};int ret=-1;for(int i=0;i<n;i++)  //枚举每一个0的位置开始向周围找陆地{for(int j=0;j<m;j++){if(grid[i][j]) continue;ret=max(ret,bfs(i,j));}}return ret;}
};

以上枚举时间为O(N^2),进行BFS的时间为O(N^2),所以如果根据根据以上写法时间复杂度就是O(N^4)在leetcode上会超时,所以能否将算法进行优化???

有多个起点,其终点是距离其最近的陆地。那能不能以陆地为起点,向外进行扩展,如果扩展到水面,那么水面到陆地的距离不久确定了吗。 但是这样不还是要对每个陆地依次进行BFS,能否让所有陆地同时进行BFS,好像确定可以,将所有陆地同时入队列,依次向外进行扩展,当一个水面第一次被扩展到时,该水面到陆地的最短距离就确定了。

以上方法就是将「多源 BFS」问题转换为「单源 BFS」问题 

图片来自宫水三叶。

class Solution {
public:int maxDistance(vector<vector<int>>& grid) {int n=grid.size();int dx[]={1,-1,0,0};int dy[]={0,0,-1,1};queue<pair<int,int>> q;for(int i=0;i<n;i++)for(int j=0;j<n;j++)if(grid[i][j]==1) q.push({i,j});  //将陆地全部入队列int ret=-1,step=0;vector<vector<int>> vist(n,vector<int>(n));while(!q.empty())    //进行BFS{int _size=q.size();step++;for(int i=0;i<_size;i++){int x=q.front().first,y=q.front().second;q.pop();for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<n&&!grid[a][b]&&!vist[a][b]){ret=max(ret,step);  //更新答案vist[a][b]=step;     //对该位置进行标记q.push({a,b});         //入队列,为下一次扩展做准备}}}}        return ret;    }
};

542. 01 矩阵

与上一题一样,此题也是一道多源BFS问题,需要转化为单元BFS,可以将0看作起点,依次向外进行扩展,扩展到1位置该位置的答案就确定了。

class Solution {
public:vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {//多源BFS,从0开始向外进行扩展int n=mat.size(),m=mat[0].size();int dx[]={0,0,-1,1};int dy[]={1,-1,0,0};queue<pair<int,int>> q;for(int i=0;i<n;i++)   //记录所有0位置for(int j=0;j<m;j++)if(mat[i][j]==0) q.push({i,j});//进行BFSint step=0;vector<vector<int>> ret(n,vector<int>(m));while(!q.empty())   //进行BFS{int _size=q.size();step++;for(int i=0;i<_size;i++){int x=q.front().first,y=q.front().second;q.pop();for(int k=0;k<4;k++){   int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<m&&mat[a][b]&&!ret[a][b]){ret[a][b]=step;  //对该位置答案进行更新q.push({a,b});}}   }}return ret;}
};

994. 腐烂的橘子

依旧是采用多源BFS,以每一个已经腐烂的桔子为起点向周围进行扩展,直到所有的桔子都腐烂或者已经不能在扩展为止。

class Solution {
public:int orangesRotting(vector<vector<int>>& grid) {//多源BFS问题int n=grid.size(),m=grid[0].size();int dx[]={0,0,-1,1};int dy[]={1,-1,0,0};//先将所有腐烂的桔子入队列queue<pair<int,int>> q;int fine=0;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(grid[i][j]==1) fine++;else if(grid[i][j]==2) q.push({i,j});}}if(fine==0) return 0;//从每个腐烂的桔子位置开始向外进行BFSint step=0;while(!q.empty()){int _size=q.size();step++;for(int i=0;i<_size;i++){int x=q.front().first,y=q.front().second;q.pop();for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]==1){if(--fine==0) return step;   //已经没有好橘子了grid[a][b]=2;           //将改新鲜橘子标记为腐烂q.push({a,b});}}}}return -1;}
};

1765. 地图中的最高点

相邻格子的高度差至多为1,已知水域的高度为0,那不就可以从水域开始向外进行扩展。

细节:必须保证每个陆地的高度都只能被更新一次,进而保证陆地的高度是最矮的,否则可能会出现相邻两个的高度差超过1的情况。

class Solution {
public:vector<vector<int>> highestPeak(vector<vector<int>>& isWater) {int n=isWater.size(),m=isWater[0].size();int dx[]={0,0,-1,1};int dy[]={1,-1,0,0};//相邻格子的高度差至多为1,已知水域的高度为0,那不就可以从水域开始向外进行扩展queue<pair<int,int>> q;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(isWater[i][j]==1){isWater[i][j]=-1;   //先将水面置为-1,如果直接将其置为0,就和陆地搞混在一起了q.push({i,j});}}}   int step=0;while(!q.empty()){int _size=q.size();step++;for(int i=0;i<_size;i++){int x=q.front().first,y=q.front().second;q.pop();for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<m&&!isWater[a][b]){isWater[a][b]=step;   //对陆地的高度进行更新,只能更新依次,报站高度差不超过1q.push({a,b});}}}}    for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(isWater[i][j]==-1) isWater[i][j]=0;  //将用-1标记的水面全部置为0return isWater;}
};

BFS面试难题剖析 

934. 最短的桥

最短的桥,要找到从一座岛到另一座岛需要填多少水域,直接让一座岛向外进行扩展,直到扩展到另一座岛的位置时,停止。使用DFS统计一座岛的所有坐标,使用BFS将一座岛向外进行扩展。

class Solution {
public:int shortestBridge(vector<vector<int>>& grid) {//将水面填平,让两座岛形成一座岛//可以让一座岛向外进行扩展,当从一座岛扩展到另一座岛的时候,其就已经填平了,直接返回扩展的次数即可int n=grid.size(),m=grid[0].size();int dx[]={0,0,1,-1};int dy[]={1,-1,0,0};//先分别对两座岛的陆地进行统计function<void(queue<pair<int,int>>&,int,int)> dfs=[&](queue<pair<int,int>>& q,int x,int y){grid[x][y]=2;   //将这个岛置为2,于另一个岛进行区分q.push({x,y});for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]==1) dfs(q,a,b);}};queue<pair<int,int>> q;int flag=1;for(int i=0;i<n&&flag;i++){for(int j=0;j<m&&flag;j++){if(grid[i][j]==1){dfs(q,i,j);flag=0;  //通过flag标记来跳出循环}}}//此时一个岛都已经进行统计了//让这个岛向外进行扩展,直到扩展到另一个岛int step=0;  //记录已经扩展的次数while(q.size()){int _size=q.size();for(int i=0;i<_size;i++){int x=q.front().first,y=q.front().second;q.pop();for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]==1) return step;if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]==0) {grid[a][b]=2;q.push({a,b});}}}step++;}return -1;}
};

2146. 价格范围内最高排名的 K 样物品

优先级:距离>价格>横坐标>纵坐标,因为是距离优先,所以考虑BFS,让距离从小到大一次向外扩展。每一次向外扩展时还需要对各个方向的数据进行排序,所以总而言之就是:BFS+排序。

class Solution {
public:vector<vector<int>> highestRankedKItems(vector<vector<int>>& grid, vector<int>& pricing, vector<int>& start, int tar) {//优先级:距离>价格>横坐标>纵坐标//因为是距离优先,所以考虑BFS,让距离从小到大一次向外扩展int n=grid.size(),m=grid[0].size();vector<vector<int>> dxy({{0,1},{0,-1},{1,0},{-1,0}}); vector<vector<int>> ret;  //返回值int low=pricing[0],heigh=pricing[1],sx=start[0],sy=start[1];queue<pair<int,int>> q;     //队列if(grid[sx][sy]>=low&&grid[sx][sy]<=heigh) ret.push_back({sx,sy});  //起点满足条件q.push({sx,sy});auto comp = [&](vector<int>& v1, vector<int>& v2){int a=grid[v1[0]][v1[1]],b=grid[v2[0]][v2[1]];  return a < b||a==b&&v1<v2;  //数组也可以进行比较,于字符串比较原理相同};vector<vector<int>> vist(n,vector<int>(m));   //存储已经到达的位置vist[sx][sy]=1;while(q.size()&&ret.size()<tar){int _size=q.size();vector<vector<int>> tmp;  //先使用一个临时数组,存放上下所有满足条件坐标for(int i=0;i<_size;i++){int x=q.front().first,y=q.front().second;q.pop();for(int k=0;k<4;k++){int a=x+dxy[k][0],b=y+dxy[k][1];if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]>=low&&grid[a][b]<=heigh&&!vist[a][b]) tmp.push_back({a,b});if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]&&!vist[a][b]) {vist[a][b]=1;q.push({a,b});}}}sort(tmp.begin(),tmp.end(),comp);   //对坐标进行排序,因为是在相同距离的坐标,所以只需要考虑234条件即可ret.insert(ret.end(),tmp.begin(),tmp.end());}  if(ret.size()>tar) ret.resize(tar);return ret;}
};

1293. 网格中的最短路径

此题的难点在于,已经扩展过的位置还扩展不扩展了???

当第二次扩展到这个位置时,消除的障碍物比上一次少才向这个位置扩展,否则不向这个位置扩展,可以通过一个int vist[n][m]的数组来记录到达该位置时消除障碍物的个数。
具体解法看以下代码:

struct Step
{int _x,_y;  //该位置的坐标int _rest;  //到达该位置时剩余可以消除障碍物的个数Step(int x,int y,int rest):_x(x),_y(y),_rest(rest){}
};class Solution {
public:int shortestPath(vector<vector<int>>& grid, int k) {//使用BFS,只不过在向外进行扩展的时候,对每个位置记录以下已经移除的障碍物的个数int n=grid.size(),m=grid[0].size(),step=0;if(n==1&&m==1) return 0;int dx[]={0,0,1,-1};int dy[]={1,-1,0,0};k=min(k,m+n-3);   //做多会移除m+n-3个障碍物,所以可以对此处进行剪枝vector<vector<int>> vist(n,vector<int>(m,-1));  //记录到达每个位置剩余移除障碍物的次数vist[0][0]=k;queue<Step> q;q.push(Step(0,0,k)); while(!q.empty()){int _size=q.size();for(int i=0;i<_size;i++){int x=q.front()._x,y=q.front()._y;int rest=q.front()._rest;if(x==n-1&&y==m-1) return step;q.pop();for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k],tmp=rest;if(a>=0&&a<n&&b>=0&&b<m){if(grid[a][b]) tmp--;   //是障碍物if(tmp<0) continue;      //依次次数超过限制if(vist[a][b]==-1||tmp>vist[a][b])   //没有到达的位置或已经达到了但是剩余次数较少{q.push(Step(a,b,tmp));vist[a][b]=tmp;}}}}step++;}return -1;}
};

909. 蛇梯棋

要求最短路径,毫无疑问考虑BFS,但是此题根据背景介绍:每个格子都有编号,编号的顺序还是蛇形的。那么必定需要将编号转换为坐标。

BFS中的队列存放什么,是存放坐标还是编号???

存放编号会更好一些,因为如果存放坐标,每次将坐标拿出来后还是需要转化为编号,再转换为坐标,多了一步转化。而存放编号的话就只需要考虑将编号转化为坐标.
 

class Solution {
public:int snakesAndLadders(vector<vector<int>>& board) {//根据题意需要找最短路径,考虑使用BFS//此题的BFS中的队列存放什么???存放左边还是编号???//用队列存储编号//根据编号pos计算横坐标:从下往上行为r=(pos-1)%n,那么从上往下就是n-1-r//计算纵坐标,如果r为偶数,纵坐标就是l=(pos-1)%n;如果r是奇数,纵坐标就是n-1-lint n=board.size(), step=0;vector<int> vist(n*n+1);  //记录那些编号已经去过了vist[1]=1;queue<int> q;  //记录编号q.push(1);while(!q.empty()){int _size=q.size();for(int i=0;i<_size;i++){int pos=q.front();q.pop();if(pos==n*n) return step;for(int k=1;k<=6&&pos+k<=n*n;k++){int new_pos = pos + k;int _r = (new_pos - 1) / n, r = n - 1 - _r;int _l = (new_pos - 1) % n;if (_r % 2 != 0) _l = n - 1 - _l;   //将编号转化为坐标if(board[r][_l]!=-1) new_pos=board[r][_l];if(vist[new_pos]) continue;   //如果该位置已经来过,就不需要再走了vist[new_pos]=1;q.push(new_pos);}}step++;}return -1;}
};

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

按照题目要求使用BFS即可,只不过:

蛇头在一个位置又两种方向,分别是向下和向右,所以在检查一个方向是否需要进入的时候就出现了分歧,上一次到达这个位置蛇向右,这一次蛇向下,怎么进行区分呢??

将二维数组vector<vector<int>> vist变成三维数组vector<vector<vector<int>>> 不仅记录已经走过的位置,还记录走到这个位置时是什么方向即可。

class Solution {
public:int minimumMoves(vector<vector<int>>& grid) {//使用BFS,只需要记录蛇身的方向int n=grid.size(),m=grid[0].size(),step=0;const int RIGHT=1;const int DOWN=2;queue<vector<int>> q;q.push({0,1,RIGHT});vector<vector<vector<int>>> vist(n,vector<vector<int>>(m,vector<int>(2)));//使用两个变量其中0表示竖直方向,1表示水平方向vist[0][1][1]=1;while(!q.empty()){int _size=q.size();for(int i=0;i<_size;i++){int x=q.front()[0],y=q.front()[1],dir=q.front()[2];q.pop();if(x==n-1&&y==n-1&&dir==RIGHT) return step;if(dir==RIGHT){//向右if(y+1<m&&!grid[x][y+1]&&!vist[x][y+1][1]) {q.push({x,y+1,RIGHT});vist[x][y+1][1]=1;}//旋转if(x + 1 < n && !grid[x + 1][y] && !grid[x + 1][y-1]&&!vist[x+1][y-1][0]){q.push({x+1,y-1,DOWN}); vist[x+1][y-1][0]=1;}//整体向下移动if(x+1<n&&!grid[x+1][y-1]&&!grid[x+1][y]&&!vist[x+1][y][1]) {q.push({x+1,y,RIGHT});vist[x+1][y][1]=1;}}else{if(x+1<n&&!grid[x+1][y]&&!vist[x+1][y][0]) {q.push({x+1,y,DOWN});vist[x+1][y][0]=1;}if(y+1<m&&!grid[x][y+1]&&!grid[x-1][y+1]&&!vist[x-1][y+1][1]){q.push({x-1,y+1,RIGHT}); vist[x-1][y+1][1]=1;}//整体向右if(y+1<n&&!grid[x-1][y+1]&&!grid[x][y+1]&&!vist[x][y+1][0]) {q.push({x,y+1,DOWN});vist[x][y+1][0]=1;}}}step++;}return -1;}
};

675. 为高尔夫比赛砍树

每一次砍掉的树必须是剩余树中最小的,那么总体来说砍树的顺序是固定的,就是从一个位置到最小高度的树的最小距离,只不过需要进行多次而已。

这不就相当于从一个a位置到另一个b位置的最短路径,再从b位置到c位置的最短路径嘛,这样此题就抽象为了多次BFS找最小路径 。
 

class Solution {
public:int cutOffTree(vector<vector<int>>& forest) {//进行模拟//使用BFS,其中BFS的起点是在改变的int n=forest.size(),m=forest[0].size();int dx[]={0,0,1,-1};int dy[]={1,-1,0,0};priority_queue<vector<int>,vector<vector<int>>,greater<vector<int>>> pq;  //对树的高度进行排序,带上坐标for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(forest[i][j]>1) pq.push({forest[i][j],i,j}); int xi=0,yi=0,tar_x,tar_y,ret=0;function<int(int,int)> bfs=[&](int _x,int _y)  //进行BFS,从一个位置找最小位置需要的步数{vector<vector<int>> vist(n,vector<int>(m));vist[_x][_y]=1;queue<pair<int,int>> q;q.push({_x,_y});int step=0;while(!q.empty()){int _size=q.size();for(int i=0;i<_size;i++){int x=q.front().first,y=q.front().second;q.pop();if(x==tar_x&&y==tar_y) return step;for(int k=0;k<4;k++){int a=x+dx[k],b=y+dy[k];if(a>=0&&a<n&&b>=0&&b<m&&forest[a][b]&&!vist[a][b]){   vist[a][b]=1;q.push({a,b});}   }}step++;}return -1;};while(pq.size()){auto tmp=pq.top();pq.pop();tar_x=tmp[1],tar_y=tmp[2];int step=bfs(xi,yi);     //求需要多少步走到最小树的位置if(step==-1) return -1;else ret+=step;xi=tar_x,yi=tar_y;  //记录下一次走的起始位置}return ret;}
};

总结

BFS类型得题目在总体而言套路都是差不多得都是使用队列来记录每一次向外进行扩展得结果,只不过每一次都需要判断能否将扩展得位置加入到队列中。

对于一些有难度得BFS题型,经常会出现一个位置多种状态,需要特别注意加入队列得判断条件。还有可能出现BFS的套用,比如最后一个高尔夫比赛砍树,将每一步进行拆分就不难发现,其每一步都是一个BFS。

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

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

相关文章

ZW3D 二次开发-创建球体

使用中望3d用户函数 cvxPartSphere 创建球体 函数定义: ZW_API_C evxErrors cvxPartSphere(svxSphereData *Sphere, int *idShape); typedef struct svxSphereData {evxBoolType Combine; /**<@brief combination method */svxPoint Center; /**<@brief sphere ce…

艺术总监的构图“再造术”:用PS生成式AI,重塑照片叙事框架

在视觉叙事中&#xff0c;我们常常面临一个核心的“对立统一”&#xff1a;一方面是**“被捕捉的瞬间”&#xff08;The Captured Moment&#xff09;&#xff0c;即摄影师在特定时间、特定地点所记录下的客观现实&#xff1b;另一方面是“被期望的叙事”**&#xff08;The Des…

ChatGPT无法登陆?分步排查指南与解决方案

ChatGPT作为全球领先的AI对话工具&#xff0c;日均处理超百万次登录请求&#xff0c;登陆问题可能导致用户无法正常使用服务&#xff0c;影响工作效率或学习进度。 无论是显示「网络错误」「账号未激活」&#xff0c;还是持续加载无响应&#xff0c;本文将从网络连接、账号状态…

用Joern执行CPGQL找到C语言中不安全函数调用的流程

1. 引入 静态应用程序安全测试&#xff08;Static application security testing&#xff09;简称SAST&#xff0c;是透过审查程式源代码来识别漏洞&#xff0c;提升软件安全性的作法。 Joern 是一个强大的开源静态应用安全测试&#xff08;SAST&#xff09;工具&#xff0c;专…

读文章 Critiques of World model

论文名称&#xff1a;对世界模型的批判 作者单位&#xff1a; CMU&#xff0c; UC SD 原文链接&#xff1a;https://arxiv.org/pdf/2507.05169 摘要&#xff1a; 世界模型&#xff08;World Model&#xff09;——即真实世界环境的算法替代物&#xff0c;是生物体所体验并与之…

利用docker部署前后端分离项目

后端部署数据库:redis部署:拉取镜像:doker pull redis运行容器:docker run -d -p 6379:6379 --name my_redis redismysql部署:拉取镜像:docker pull mysql运行容器:我这里3306被占了就用的39001映射docker run -d -p 39001:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /hom…

YOLOv11调参指南

YOLOv11调参 1. YOLOv11参数体系概述 YOLOv11作为目标检测领域的前沿算法&#xff0c;其参数体系可分为四大核心模块&#xff1a; 模型结构参数&#xff1a;决定网络深度、宽度、特征融合方式训练参数&#xff1a;控制学习率、优化器、数据增强策略检测参数&#xff1a;影响预测…

云原生核心技术解析:Docker vs Kubernetes vs Docker Compose

云原生核心技术解析&#xff1a;Docker vs Kubernetes vs Docker Compose &#x1f6a2;☸️⚙️ 一、云原生核心概念 ☁️ 云原生&#xff08;Cloud Native&#xff09; 是一种基于云计算模型构建和运行应用的方法论&#xff0c;核心目标是通过以下技术实现弹性、可扩展、高可…

keepalive模拟操作部署

目录 keepalived双机热备 一、配置准备 二、配置双机热备&#xff08;基于nginx&#xff09; web1端 修改配置文件 配置脚本文件 web2端 修改配置文件 配置脚本文件 模拟检测 开启keepalived服务 访问结果 故障模拟 中止nginx 查看IP 访问浏览器 重启服务后…

Java 中的 volatile 是什么?

&#x1f449; volatile &#xff1a;不稳定的 英[ˈvɒlətaɪl] 美[ˈvɑːlətl] adj. 不稳定的;<计>易失的;易挥发的&#xff0c;易发散的;爆发性的&#xff0c;爆炸性的;易变的&#xff0c;无定性的&#xff0c;无常性的;短暂的&#xff0c;片刻的;活泼的&#xff…

MongoDB性能优化实战指南:原理、实践与案例

MongoDB性能优化实战指南&#xff1a;原理、实践与案例 在大规模数据存储与查询场景下&#xff0c;MongoDB凭借其灵活的文档模型和水平扩展能力&#xff0c;成为众多互联网及企业级应用的首选。然而&#xff0c;在生产环境中&#xff0c;随着数据量和并发的增长&#xff0c;如何…

细谈kotlin中缀表达式

Kotlin 是一种适应你编程风格的语言&#xff0c;允许你在想什么时候写代码就什么时候写代码。Kotlin 提供了一些机制&#xff0c;帮助我们编写易读易懂的代码。其中一个非常有趣的机制是 中缀表达式&#xff08;infix notation&#xff09;。它允许我们定义和调用函数时省略点号…

[Nagios Core] CGI接口 | 状态数据管理.dat | 性能优化

链接&#xff1a;https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/ docs&#xff1a;Nagios Core Nagios Core 是功能强大的基础设施监控系统&#xff0c;包含 CGI 程序&#xff0c;允许用户通过 Web 界面查看当前状态、历史记录等。通过以下技术栈实现…

Linux进程优先级机制深度解析:从Nice值到实时调度

前言 在Linux系统中&#xff0c;进程优先级决定了CPU资源的分配顺序&#xff0c;直接影响系统性能和关键任务的响应速度。无论是优化服务器负载、确保实时任务稳定运行&#xff0c;还是避免低优先级进程拖慢系统&#xff0c;合理调整进程优先级都是系统管理和性能调优的重要技能…

深入浅出Kafka Broker源码解析(下篇):副本机制与控制器

一、副本机制深度解析 1.1 ISR机制实现 1.1.1 ISR管理核心逻辑 ISR&#xff08;In-Sync Replicas&#xff09;是Kafka保证数据一致性的核心机制&#xff0c;其实现主要分布在ReplicaManager和Partition类中&#xff1a; public class ReplicaManager {// ISR变更集合&#xff0…

Fluent许可文件安装和配置

在使用Fluent软件进行流体动力学模拟之前&#xff0c;正确安装和配置Fluent许可文件是至关重要的一步。本文将为您提供详细的Fluent许可文件安装和配置指南&#xff0c;帮助您轻松完成许可文件的安装和配置&#xff0c;确保Fluent软件能够顺利运行。 一、Fluent许可文件安装步骤…

Python----大模型( RAG的文本分割,文本分割方法 )

一、RAG文本分割RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;模型是一种结合了检索 和生成能力的自然语言处理模型。 它通过检索相关的文档片段&#xff0c;并将这些信息作为生成过程的上下文&#xff0c;以提高生成质量 和准确性。在R…

vue笔记3 VueRouter VueX详细讲解

vueRouter & vueX 看到这里的朋友如果没有看过前几期&#xff0c;可以通过文章的链接跳转到第一期&#xff0c;从第一期的 vue2 语法开始学习&#xff0c;如果是复习的朋友&#xff0c;也可以看本期只学习 vueRouter & VueX 项目初始化 经过上期&#xff0c;我们学习…

从当下需求聊聊Apifox 与 Apipost 的差异

作为一名长期投身于复杂项目开发的工程师&#xff0c;我深切体会到一款适配的接口管理工具对提升开发效率的关键意义。当团队在进行工具选型时&#xff0c;我对 Apifox 和 Apipost 展开了全面且系统的对比分析&#xff0c;其中的诸多发现&#xff0c;值得与大家深入探讨。 一、…

蓝牙协议栈高危漏洞曝光,攻击可入侵奔驰、大众和斯柯达车载娱乐系统

OpenSynergy BlueSDK关键漏洞&#xff0c;可远程执行代码入侵数百万车辆系统PCA网络安全公司的研究人员在OpenSynergy BlueSDK蓝牙协议栈中发现了一组被统称为"完美蓝"&#xff08;PerfektBlue&#xff09;的关键漏洞。利用这些漏洞可能对数百万辆汽车实施远程代码执…