"绝境之中才窥见,Winner,Winner"
FloodFill算法简介:
floodfill又翻译成漫水填充。我们可以将下面的矩阵理解为一片具有一定高度的坡地,此时突发洪水,洪水会将高度<0的地方填满。
话句话来说,FloodFill算法是为了找出那些具有相同性质的区域。那我们现在就来看看一些经典的使用floodfill算法的题目。
图像渲染
(1) 题目解析
这类题目的代码部分,都是将模拟的过程转化成代码。
(2) 算法代码
dfs:
class Solution {
public:int m,n;int prev;int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {m = image.size(),n = image[0].size();if(image[sr][sc] == color) return image;prev = image[sr][sc];dfs(image,sr,sc,color);return image;}void dfs(vector<vector<int>>& image, int i,int j,int newcolor){// 从这一点出发,每一个方向去 递归相邻节点image[i][j] = newcolor;for(int k=0;k<4;++k){int x=i+dx[k],y = j+dy[k];// 满足 要渲染更改的值 if(x>=0 && x<m && y>=0 && y<n && image[x][y] == prev){dfs(image,x,y,newcolor);}}}
};
bfs:
class Solution {
public:typedef pair<int,int> PII;int m,n;int prev;int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {m = image.size(),n = image[0].size();if(image[sr][sc] == color) return image;// 当前位置会被修改为newcolor 这里是保存需要被渲染的值prev = image[sr][sc];bfs(image,sr,sc,color);return image;}void bfs(vector<vector<int>>& image, int i,int j,int newcolor){queue<PII> que;image[i][j] = newcolor;que.push({i,j});while(que.size()){// 取出已经被渲染的位置,并将它的相邻位置,满足条件的 插入进队列auto [a,b] = que.front();que.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 && image[x][y] == prev){image[x][y] = newcolor;que.push({x,y});}}}}
};
岛屿数量
(1) 题目解析
注: 进行bfs或dfs时需要注意,防止统计已经遍历过的位置。我们有两种解决方法,其一就是对原数组内部的值进行修改,区别于目标条件,其二就是创建一个新的布尔数组,标记每一个遍历过的点。
(2) 算法代码
dfs:
class Solution {
public:int m,n;bool vis[301][301];int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};int numIslands(vector<vector<char>>& grid) {m = grid.size(),n = grid[0].size();int count = 0;for(int i=0;i<m;++i)for(int j=0;j<n;++j){// 遍历到岛屿进行一次dfs 并且这个位置没有被访问过if(grid[i][j] == '1' && !vis[i][j]) {count++;vis[i][j] = true; // 标记dfs(grid,i,j);}}return count;}void dfs(vector<vector<char>>& grid,int i,int j){// 这里的dfs 只需将与(i,j) 相连的岛屿统计即可for(int k=0;k<4;++k){int x=i+dx[k],y=j+dy[k];if(x>=0 && x<m && y>=0 && y<n && grid[x][y] == '1' && !vis[x][y]){vis[x][y] = true;dfs(grid,x,y);}}}
};
bfs:
class Solution {public:int m,n;bool vis[301][301];int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};int numIslands(vector<vector<char>>& grid) {m = grid.size(),n = grid[0].size();int count = 0;for(int i=0;i<m;++i)for(int j=0;j<n;++j){// 遍历到岛屿进行一次dfs 并且这个位置没有被访问过if(grid[i][j] == '1' && !vis[i][j]) {count++;vis[i][j] = true; // 标记bfs(grid,i,j);}}return count;}void bfs(vector<vector<char>>& grid,int i,int j){// 这里的bfs 只需将与(i,j) 相连的岛屿统计即可// bfs通常需要借助队列来实现queue<pair<int,int>> que;que.push({i,j});while(que.size()){auto [a,b] = que.front();que.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' && !vis[x][y]){vis[x][y] = true;que.push({x,y});}}}}};
岛屿的最大面积
(1) 题目解析
这道题和上个题目十分类似,只不过这道题目的结果是求这些岛屿的最大面积,所以,我们需要对相连岛屿数量进行统计,并最终返回一个最大值。
(2) 算法代码
dfs:
class Solution {
public:int m,n;bool vis[301][301];int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};int ret,count;int maxAreaOfIsland(vector<vector<int>>& grid) {m = grid.size(),n = grid[0].size(),ret = 0;for(int i=0;i<m;++i)for(int j=0;j<n;++j){// 遍历到岛屿进行一次dfs 并且这个位置没有被访问过if(grid[i][j] == 1 && !vis[i][j]) {count = 0;dfs(grid,i,j);ret = max(ret,count);}}return ret;}void dfs(vector<vector<int>>& grid,int i,int j){vis[i][j] = true;count++;for(int k=0;k<4;++k){int x=i+dx[k],y=j+dy[k];if(x>=0 && x<m && y>=0 && y<n && grid[x][y] == 1 && !vis[x][y]){ dfs(grid,x,y);}}}
};
bfs:
class Solution {
public:int m,n;bool vis[301][301];int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};int ret,count;int maxAreaOfIsland(vector<vector<int>>& grid) {m = grid.size(),n = grid[0].size(),ret = 0;for(int i=0;i<m;++i)for(int j=0;j<n;++j){// 遍历到岛屿进行一次dfs 并且这个位置没有被访问过if(grid[i][j] == 1 && !vis[i][j]) {count = 0;vis[i][j] = true; // 标记bfs(grid,i,j);ret = max(ret,count);}}return ret;}void bfs(vector<vector<int>>& grid,int i,int j){// 这里的bfs 只需将与(i,j) 相连的岛屿统计即可// bfs通常需要借助队列来实现queue<pair<int,int>> que;que.push({i,j});count++;while(que.size()){auto [a,b] = que.front();que.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 && !vis[x][y]){count++;vis[x][y] = true;que.push({x,y});}}}}
};
被围绕的区域
(1) 题目解析
但其实,我们发现这两个dfs本质是做同样的事情,但却因为判断边界,我们需要写两份不同的dfs,这是蛮麻烦的事情。我们换个思路,直接用一个dfs将边界的'O',进行过滤,并填上不相关的字符,再将整个数组遍历,遇到‘O’改为‘X’,遇到这个不相关的字符,改为'O'。
(2) 算法代码
dfs:
class Solution {
public:int m,n;bool vis[201][201];int dx[4] = {0,0,-1,1};int dy[4] = {1,-1,0,0};void solve(vector<vector<char>>& board) {m = board.size(),n = board[0].size();// 处理第一行和最后一行for(int j=0;j<n;++j){if(board[0][j] == 'O') dfs(board,0,j);if(board[m-1][j] == 'O') dfs(board,m-1,j);}// 第一列 最后一列for(int i=0;i<m;++i){if(board[i][0] == 'O') dfs(board,i,0);if(board[i][n-1] == 'O') dfs(board,i,n-1);}for(int i=0;i<m;++i)for(int j=0;j<n;++j){if(board[i][j] == '.') board[i][j] = 'O';else if(board[i][j] == 'O') board[i][j] = 'X';}}void dfs(vector<vector<char>>& board,int i,int j){board[i][j] = '.';for(int k=0;k<4;++k){int x=i+dx[k],y=j+dy[k];if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'O'){dfs(board,x,y);}}}
};
bfs:
class Solution {
public:int m,n;bool vis[201][201];int dx[4] = {0,0,-1,1};int dy[4] = {1,-1,0,0};void solve(vector<vector<char>>& board) {m = board.size(),n = board[0].size();// 处理第一行和最后一行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);}// 第一列 最后一列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] == '.') board[i][j] = 'O';else if(board[i][j] == 'O') board[i][j] = 'X';}}void bfs(vector<vector<char>>& board,int i,int j){board[i][j] = '.';queue<pair<int,int>> que;que.push({i,j});while(que.size()){auto [a,b] = que.front();que.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'){board[x][y] = '.';que.push({x,y});}}}}
};
扫雷
(1) 题目解析
经典的扫雷游戏,至于玩法也就不过多解释。该题目考察的地方就在于,点击一个点之后,从这个 点开始进行对相邻领域的展开,我们可以采用dfs或bfs完成。
当挖出的位置是一个雷,将这个位置标记成‘X’,游戏结束。当挖出的一个地方是空位置,你需要首先去相邻区域查找是否有雷,如果没有发现雷,则标记为'B',否则就需要将搜查到的雷的个数,标记在该位置处。
(2) 算法代码
dfs:
class Solution {
public:int m,n;bool vis[51][51];// 扫雷位置有八个位置需要被展开int dx[8] = {0,0,-1,1,-1,1,-1,1};int dy[8] = {1,-1,0,0,1,-1,-1,1};vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {m = board.size(),n = board[0].size();int i = click[0],j = click[1];if(board[i][j] == 'M')board[i][j] = 'X';elsedfs(board,i,j);return board;}void dfs(vector<vector<char>>& board,int i,int j){int count = 0;// 统计雷的数量for(int k=0;k<8;++k){int x=i+dx[k],y=j+dy[k];if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'M') count++;}if(count){board[i][j] = count + '0';}else{board[i][j] = 'B';for(int k=0;k<8;++k){int x=i+dx[k],y=j+dy[k];if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'E') dfs(board,x,y);}}}
};
bfs:
class Solution {
public:int m,n;bool vis[51][51];// 扫雷位置有八个位置需要被展开int dx[8] = {0,0,-1,1,-1,1,-1,1};int dy[8] = {1,-1,0,0,1,-1,-1,1};vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {m = board.size(),n = board[0].size();int i = click[0],j = click[1];if(board[i][j] == 'M')board[i][j] = 'X';elsebfs(board,i,j);return board;}void bfs(vector<vector<char>>& board,int i,int j){queue<pair<int,int>> que;que.push({i,j});vis[i][j] = true;while(que.size()){auto [a,b] = que.front();que.pop();int count = 0;// 统计雷的数量for(int k=0;k<8;++k){int x=a+dx[k],y=b+dy[k];if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'M') count++;}if(count){board[a][b] = count + '0';}else{board[a][b] = 'B';for(int k=0;k<8;++k){int x=a+dx[k],y=b+dy[k];if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'E'){// 这里改为'B' 避免被其他点统计到 队列中去// 也可以使用 vis[x][y] = ture; 进行标记 不被统计 !!!board[x][y] = 'B'; que.push({x,y});}}}}}
};
本篇到此结束,感谢你的阅读。
祝你好运,向阳而生~