广搜的搜索方式就适合于解决两个点之间的最短路径问题。
因为广搜是从起点出发,以起始点为中心一圈一圈进行搜索,一旦遇到终点,记录之前走过的节点就是一条最短路。当然,也有一些问题是广搜 和 深搜都可以解决的,例如岛屿问题,这类问题的特征就是不涉及具体的遍历方式,只要能把相邻且相同属性的节点标记上就行。
一圈一圈的搜索过程放入队列,保证每一圈都是一个方向去转,例如统一顺时针或者逆时针。
模板:
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
// grid 是地图,也就是一个二维数组
// visited标记访问过的节点,不要重复访问
// x,y 表示开始搜索节点的下标
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
queue<pair<int, int>> que; // 定义队列
que.push({x, y}); // 起始节点加入队列
visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点
while(!que.empty()) { // 开始遍历队列里的元素
pair<int ,int> cur = que.front(); que.pop(); // 从队列取元素
int curx = cur.first;
int cury = cur.second; // 当前节点坐标
for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 坐标越界了,直接跳过
if (!visited[nextx][nexty]) { // 如果节点没被访问过
que.push({nextx, nexty}); // 队列添加该节点为下一轮要遍历的节点
visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
}
}
}
}
四周可以改成:
dx[4] = {0,1, 0, -1};
dy[4] = {1, 0, -1, 0};
更好理解一点
例题:岛屿数量 力扣链接:https://leetcode.cn/problems/number-of-islands/description/
给你一个由 '1'
(陆地)和 '0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
这个的重点就是,用广度优先搜索标记所有节点四周的节点,然后在main()函数中,碰到标记过的陆地节点不加一,碰到全新未标记的就说明不相邻,再加一,表示多了一个岛屿区域
题解:
class Solution {
public:
int dx[4] = {0,1, 0, -1};
int dy[4] = {1, 0, -1, 0};
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y){//x,y是坐标
queue<pair<int,int>> que;
que.push({x,y});
//加进队列就标记
visited[x][y] = true;
while(!que.empty()){//遍历队列里每个节点
pair<int,int> cur = que.front();
que.pop();
int curx = cur.first;
int cury = cur.second;
for(int i=0; i<4; i++){//四周扩散
int nextx = curx+dx[i];
int nexty = cury+dy[i];
//边界条件
if(nextx<0||nextx>=grid.size()||nexty<0||nexty>=grid[0].size()){
continue;//往别的方向走
}
if(!visited[nextx][nexty] && grid[nextx][nexty] == '1'){//是没有访问过的陆地,加入队列
que.push({nextx,nexty});
visited[nextx][nexty] = true;//设置为访问过
}
}
}
}
int numIslands(vector<vector<char>>& grid) {
int result=0;
int line = grid.size();
int column = grid[0].size();
vector<vector<bool>> visited(line,vector<bool>(column,false));
for(int i=0; i<line; i++){
for(int j=0; j<column; j++){
if(!visited[i][j] && grid[i][j] == '1'){
result++;
bfs(grid,visited,i,j);//遇到没访问过的才进行bfs
}
}
}
return result;
}
};
易错的地方:
(1)visited数组没有引用传参
(2)main()函数中一定要在没有访问过的陆地再进行bfs
(3)只要一加入队列,立刻标记为true!否则可能重复(没太搞懂为啥,先记着)
DFS版本:
(没太懂为啥不用回溯,可能提前就界定了条件吧)
class Solution {
public:
int dx[4] = {0,1, 0, -1};
int dy[4] = {1, 0, -1, 0};
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y){//x,y是坐标
queue<pair<int,int>> que;
que.push({x,y});
//加进队列就标记
visited[x][y] = true;
while(!que.empty()){//遍历队列里每个节点
pair<int,int> cur = que.front();
que.pop();
int curx = cur.first;
int cury = cur.second;
for(int i=0; i<4; i++){//四周扩散
int nextx = curx+dx[i];
int nexty = cury+dy[i];
//边界条件
if(nextx<0||nextx>=grid.size()||nexty<0||nexty>=grid[0].size()){
continue;//往别的方向走
}
if(!visited[nextx][nexty] && grid[nextx][nexty] == '1'){//是没有访问过的陆地,加入队列
que.push({nextx,nexty});
visited[nextx][nexty] = true;//设置为访问过
}
}
}
}
int numIslands(vector<vector<char>>& grid) {
int result=0;
int line = grid.size();
int column = grid[0].size();
vector<vector<bool>> visited(line,vector<bool>(column,false));
for(int i=0; i<line; i++){
for(int j=0; j<column; j++){
if(!visited[i][j] && grid[i][j] == '1'){
result++;
bfs(grid,visited,i,j);//遇到没访问过的才进行bfs
}
}
}
return result;
}
};