BFS解决FloodFill算法
1.图像渲染

FloodFill用BFS解决,从某个元素开始,入队列;执行一系列操作;再准备两个数组dx,dy,辅助遍历上下左右相邻元素。
有时也需要同样规模的标记数组visited,例如imagex y已被遍历,则对应visitedx y记为1,避免重复遍历陷入死循环。本题可以不使用标记数组,如果初始坐标的原始颜色与color相同时,直接结束返回image即可。
cpp
class Solution {
public:
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color)
{
int start = image[sr][sc];
if (start == color)
return image;
int m = image.size(), n = image[0].size();
int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };
queue<pair<int, int>> q;
q.push({ sr,sc });
while (!q.empty())
{
auto [a, b] = q.front();//C++17
q.pop();
image[a][b] = color;
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) && image[x][y] == start)
q.push({ x,y });
}
}
return image;
}
};
2.岛屿数量

与上一题类似,此题要用到visited数组。在用dx,dy辅助遍历数组时,新坐标入队列后应该立刻在visited数组中记为1,否则会超时。
cpp
class Solution
{
int dx[4] = { 1,-1,0,0 };
int dy[4] = { 0,0,1,-1 };
int visited[300][300] = { 0 };
public:
int numIslands(vector<vector<char>>& grid)
{
int m = grid.size(), n = grid[0].size();
int ret = 0;
queue<pair<int, int>> q;
for (int i = 0;i < m;i++)
{
for (int j = 0;j < n;j++)
{
if (grid[i][j] == '0' || visited[i][j] == 1)
continue;
q.push({ i,j });
visited[i][j] = 1;
while (!q.empty())
{
auto [a, b] = q.front();
q.pop();
for (int k = 0;k < 4;k++)
{
int x = a + dx[k], y = b + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1' && visited[x][y] == 0)
{
q.push({ x,y });
visited[x][y] = 1;
}
}
}
ret++;
}
}
return ret;
}
};
3.岛屿的最大面积

只需要在BFS过程中记录面积即可。
cpp
class Solution
{
int dx[4] = { 0,0,1,-1 };
int dy[4] = { 1,-1,0,0 };
int visited[50][50] = { 0 };
public:
int maxAreaOfIsland(vector<vector<int>>& grid)
{
int m = grid.size(), n = grid[0].size();
int ret = 0;
queue<pair<int, int>> q;
for (int i = 0;i < m;i++)
{
for (int j = 0;j < n;j++)
{
if (grid[i][j] == 0 || visited[i][j] == 1)
continue;
int s = 0; //面积
q.push({ i,j });
visited[i][j] = 1;
while (!q.empty())
{
auto [a, b] = q.front();
q.pop();
s++;
for (int k = 0;k < 4;k++)
{
int x = a + dx[k], y = b + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && visited[x][y] == 0)
{
q.push({ x,y });
visited[x][y] = 1;
}
}
}
ret = max(ret, s);
}
}
return ret;
}
};
4.被围绕的区域

理解题意,上下左右相邻且元素都为字符O视作一个连通块,如果这个连通块被字符X包围,则把这个连通块的所有元素改为字符X;如果连通块中存在元素位于矩阵的四个边缘,则该连通块不被字符X包围。
遍历矩阵,对每个连通块BFS,如果被字符X完全包围,则进行处理,否则不处理。显然这个方法是比较麻烦的,我们可以采用正难则反的思想,先遍历矩阵的四个边缘,把字符O的连通块全部改为字符 ! ,再遍历矩阵,此时矩阵内的字符O连通块都必定是被字符X包围的,直接把字符O改为字符X,同时把字符 ! 恢复为字符O。
cpp
class Solution
{
int dx[4] = { 0,0,-1,1 };
int dy[4] = { 1,-1,0,0 };
int m = 0, n = 0;
public:
void solve(vector<vector<char>>& board)
{
m = board.size(), n = board[0].size();
//遍历0行、m-1行
for (int j = 0;j < n;j++)
{
if (board[0][j] == 'O')
BFS(board, 0, j);
if (board[m - 1][j] == 'O')
BFS(board, m - 1, j);
}
//遍历0列、n-1列
for (int i = 0;i < m;i++)
{
if (board[i][0] == 'O')
BFS(board, i, 0);
if (board[i][n - 1] == 'O')
BFS(board, i, n - 1);
}
for (int i = 0;i < m;i++)
{
for (int j = 0;j < n;j++)
{
if (board[i][j] == 'O')
board[i][j] = 'X';
if (board[i][j] == '!')
board[i][j] = 'O';
}
}
}
void BFS(vector<vector<char>>& board, int i, int j)
{
queue<pair<int, int>> q;
q.push({ i,j });
board[i][j] = '!';
while (!q.empty())
{
auto [a, b] = q.front();
q.pop();
for (int k = 0;k < 4;k++)
{
int x = a + dx[k], y = b + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O')
{
q.push({ x,y });
board[x][y] = '!';
}
}
}
}
};