BFS解决最短路径问题
注意:用队列进行BFS 求解最短路径时,需要按 “层” 处理节点 —— 同一层的所有节点属于 “同一步”,需一次性次性处理完当前层的所有节点后,再进入下一层。与floodfill算法区别在于最短路径问题需要记录层数(步数),而 Flood Fill 更关注是否遍历完所有连通节点。
一. (1926.) 迷宫中离入口最近的出口
利⽤层序遍历来解决迷宫问题,是最经典的做法。
我们可以从起点开始层序遍历,并且在遍历的过程中记录当前遍历的层数,并且标记已经遍历过的位置,避免重复计算。这样就能在找到出⼝的时候,得到起点到出⼝的最短距离
class Solution {typedef pair<int,int> pir;int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};bool v[100][100];//用于标记每一位状态是否遍历过,定义全局变量会自动初始化为0
public:int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {int ret=0;//记录最短路径queue<pir> q;//辅助宽搜int m=maze.size(),n=maze[0].size();q.push({entrance[0],entrance[1]});v[entrance[0]][entrance[1]]=true;while(q.size()){ret++;int sz=q.size();for(int j=0;j<sz;j++)//每次都要把队列中的元素删除完,这样算走一步,与floodfill算法中依次遍历并删除不同{auto[a,b]=q.front();q.pop(); for(int i=0;i<4;i++){int x=a+dx[i],y=b+dy[i];if(x>=0&&x<m&&y>=0&&y<n&&maze[x][y]=='.'&&!v[x][y]){if(x==0||x==m-1||y==0||y==n-1) //判断是否到达边界即出口{return ret;}q.push({x,y});v[x][y]=true;}} }}return -1;}
};
二. (433.) 最小基因变化
如果将每次字符串的变换抽象成图中的两个顶点和⼀条边的话,问题就变成了边权为 1的最短路问题。因此,从起始的字符串开始,来⼀次 bfs 即可。
class Solution {char d[4]={'A','C','G','T'};
public:int minMutation(string startGene, string endGene, vector<string>& bank) {unordered_set<string> bk;//预处理基因库unordered_set<string> v;//标记是否遍历过//预处理for(auto s:bank){bk.insert(s);}//处理边界情况queue<string> q;v.insert(startGene);q.push(startGene); int ret=0;//bfswhile(q.size()){ret++;int sz=q.size();while(sz--){auto s=q.front();v.insert(s);q.pop();for(int i=0;i<8;i++)//遍历字符串中每一位{string tmp=s;//避免原字符串被修改for(int j=0;j<4;j++)//遍历每一位发生基因变化可能的情况{tmp[i]=d[j];if(bk.count(tmp)&&!v.count(tmp)){if(tmp==endGene) return ret;//条件满足直接返回q.push(tmp);v.insert(tmp);}}}}}return -1;}
};
三.(127.) 单词接龙
思路与二相同,转化为bfs的最短路径问题
class Solution {
public:int ladderLength(string beginWord, string endWord, vector<string>& wordList) {unordered_set<string> hash(wordList.begin(),wordList.end());unordered_set<string> v;//判断单词是否被遍历//处理边界情况if(!hash.count(endWord)) return 0;queue<string> q;q.push(beginWord);int ret=1;while(q.size())//进行bfs{ret++;int sz=q.size();while(sz--)//遍历每一层元素{auto s=q.front();v.insert(s);q.pop();for(int k=0;k<s.size();k++)//遍历单词每一位的变化{string tmp=s;for(char i='a';i<='z';i++)//单词每一位变化的情况{tmp[k]=i;if(hash.count(tmp)&&!v.count(tmp)){if(tmp==endWord) return ret;//符合条件直接退出q.push(tmp);v.insert(tmp);}}}}}return 0;}
};
四. (675.) 为高尔夫比赛砍树
1.先找出砍树的顺序;
2. 然后按照砍树的顺序,⼀个⼀个的⽤ bfs 求出最短路即可
class Solution {int m, n;int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};bool vis[50][50];public:int cutOffTree(vector<vector<int>>& f) {m = f.size(), n = f[0].size();vector<pair<int, int>> v;for (int i = 0; i < m; i++)for (int j = 0; j < n; j++)if (f[i][j] > 1)v.push_back({i, j});//将可以砍的树入队列// 找出砍树的顺序sort(v.begin(), v.end(),[&](const pair<int, int>& p1, const pair<int, int>& p2) {return f[p1.first][p1.second] < f[p2.first][p2.second];});// 按顺序砍树int ba = 0, bb = 0;//初始位置int ret = 0;for (auto& [a, b] : v) {int r = bfs(f, ba, bb, a, b);if (r == -1)return -1;ret += r;ba = a, bb = b;//更新初始位置}return ret;}int bfs(vector<vector<int>>& f, int bx, int by, int ex, int ey) {if (bx == ex && by == ey) return 0;memset(vis, 0, sizeof(vis)); // 会重复调用bfs所以每次要清零queue<pair<int, int>> q;q.push({bx, by});int ret = 0;//记录步数while (q.size()) {ret++;int sz = q.size();//记录层数,每一层中都只算一步while (sz--) {auto [a, b] = q.front();q.pop();vis[a][b] = true;for (int i = 0; i < 4; i++) {//遍历每一步可能的方向int x = a + dx[i], y = b + dy[i];if (x >= 0 && x < m && y >= 0 && y < n && f[x][y] >= 1 &&!vis[x][y]) {//if(f[x][y]==f[ex][ey]) return ret;if (x == ex && y == ey) return ret;q.push({x, y});vis[x][y] = true;}}}}return -1;}
};