
文章目录
所谓FloodFill算法,本质上就是一种类似于洪水漫过的思想,即把题目中符合要求的区域都遍历到,而且内部是一个个的连通块

一、图像渲染
这道题就是给你一个起始坐标,要求寻找和这个起始坐标相邻的连通块(只能是上下左右相邻)
我们可以从起始坐标出发,进行深度优先遍历,修改为题目中要求的目标值
并且把我们走过的路都标记上,避免无限递归(重复走)
对于特殊情况,比如题目传的是1,无需修改,直接返回即可
java
class Solution {
int height;
int wide;
//全局变量目标值
int target;
//中心区域的值
int primitive;
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
if(image[sr][sc] == color){
//中心区域值已经和周围区域值相同
//此时无需着色
return image;
}
height = image.length;
wide = image[0].length;
primitive = image[sr][sc];
target = color;
dfs(image,sr,sc);
return image;
}
//同理使用向量数组
int [] x = {0,0,1,-1};
int [] y = {1,-1,0,0};
//posx表示第几行,posy表示第几列
private void dfs(int [][] image,int posx,int posy){
image[posx][posy] = target;
for(int i = 0;i < 4;i++){
int curX = posx+x[i];
int curY = posy+y[i];
if(curX >= 0 && curX < height && curY >= 0 && curY < wide && image[curX][curY] == primitive){
dfs(image,curX,curY);
}
}
}
}
二、岛屿数量
这一题就是,我们扫描整个表,找到一个1的位置,从这个位置开始深度优先遍历,寻找一个个岛屿(连通块)
当我们扫描表的时候,如果这个地方是1,但是已经被扫描过了,就无需深度优先遍历
java
class Solution {
boolean [][] isUse;
int height;
int wide;
//全局计数器,标记岛屿数量
int count;
public int numIslands(char[][] grid) {
height = grid.length;
wide = grid[0].length;
isUse = new boolean[height][wide];
for(int i = 0;i < height;i++){
for(int j = 0;j < wide;j++){
if(grid[i][j] == '1' && !isUse[i][j]){
//说明此时是一块新的岛屿
dfs(grid,i,j);
count++;
}
}
}
return count;
}
//同理使用向量数组
int [] x = {0,0,1,-1};
int [] y = {1,-1,0,0};
//同理posx表示行,posy表示列
private void dfs(char [][] grid,int posx,int posy){
for(int i = 0;i < 4;i++){
int curX = posx+x[i];
int curY = posy+y[i];
if(curX >= 0 && curX < height && curY >= 0 && curY < wide && grid[curX][curY] == '1' && !isUse[curX][curY]){
//标记后,并递归
isUse[curX][curY] = true;
dfs(grid, curX, curY);
}
}
}
}
三、最大岛屿面积
这题和上一题思路一样,只不过我们要记录面积而已,只需要给一个变量统计面积,并且比较返回最大值就好
java
class Solution {
boolean [][] isUse;
int height;
int wide;
//记录结果
int maxArea;
//接收每一次递归的值
int dfsNum;
public int maxAreaOfIsland(int[][] grid) {
height = grid.length;
wide = grid[0].length;
isUse = new boolean[height][wide];
//先去寻找第一个岛
for(int i = 0;i < height;i++){
for(int j = 0;j < wide;j++){
if(grid[i][j] == 1 && !isUse[i][j]){
//本身就是一个面积,递归的时候传入1,不要忘记了
//并且也要标记为使用
dfsNum = 1;
isUse[i][j] = true;
dfs(grid,i,j,1);
maxArea = Math.max(dfsNum,maxArea);
}
}
}
return maxArea;
}
//同理使用向量数组
int [] x = {0,0,1,-1};
int [] y = {1,-1,0,0};
//同理posx表示行,posy表示列
private void dfs(int [][] grid,int posx,int posy,int pathCount){
for(int i = 0;i < 4;i++){
int curX = posx+x[i];
int curY = posy+y[i];
if(curX >= 0 && curX < height && curY >= 0 && curY < wide && grid[curX][curY] == 1 && !isUse[curX][curY]){
pathCount++;
dfsNum++;
isUse[curX][curY] = true;
dfs(grid, curX, curY, pathCount);
}
}
}
}
四、被围绕的区域
这一题难点就在于对于边缘情况的处理
如果你的想法是边缘就边缘呗,我到时候回溯的时候恢复现场即可
但是这样写太复杂了,你怎么知道它是边缘的区域呢
因此,我们采用反思路,既然边缘是特殊情况,那我优先把边缘的区域处理掉
剩下的内部不就是我们想要的区域吗
java
class Solution {
boolean [][] isUse;
int height;
int wide;
public void solve(char[][] board) {
height = board.length;
wide = board[0].length;
isUse = new boolean[height][wide];
//先去扫描边界
for(int i = 0;i < wide;i++){
//第一行和最后一行扫描
if(board[0][i] == 'O' && !isUse[0][i]){
isUse[0][i] = true;
dfs(board,0,i);
}
if(board[height-1][i] == 'O' && !isUse[height-1][i]){
isUse[height-1][i] = true;
dfs(board,height-1,i);
}
}
for(int j = 0;j < height;j++){
//第一列和最后一列扫描
if(board[j][0] == 'O' && !isUse[j][0]){
isUse[j][0] = true;
dfs(board,j,0);
}
if(board[j][wide-1] == 'O' && !isUse[j][wide-1]){
isUse[j][wide-1] = true;
dfs(board,j,wide-1);
}
}
//还原被修改的边界连通块
for(int i = 0;i < height;i++){
for(int j = 0;j < wide;j++){
if(isUse[i][j]){
//说明是处理边界的连通块
board[i][j] = 'O';
}
if(board[i][j] == 'O' && !isUse[i][j]){
//说明是内部的连通块
board[i][j] = 'X';
}
}
}
}
//同理使用向量数组
int [] x = {1,-1,0,0};
int [] y = {0,0,1,-1};
//同理posx表示第几行,posy表示第几列
private void dfs(char [][] board,int posx,int posy){
for(int i = 0;i < 4;i++){
int curX = posx+x[i];
int curY = posy+y[i];
if(curX >= 0 && curX < height && curY >= 0 && curY < wide && board[curX][curY] == 'O' && !isUse[curX][curY]){
isUse[curX][curY] = true;
board[curX][curY] = 'X';
dfs(board, curX, curY);
}
}
}
}
五、太平洋大西洋水流问题
这一题起始还是和我们上一题一样的思路,既然我们判断从高处流向两个洋很难实现
那我们为什么不分别让两个洋的水往高处流呢?
这样我们两个洋水往高处流的相交区域,不就是题目要的可以流向两个洋的区域吗

java
class Solution {
boolean [][] isFloodPacific;
boolean [][] isFloodAtlantic;
int height;
int wide;
public List<List<Integer>> pacificAtlantic(int[][] heights) {
//这题采用反思路,既然是流向两个洋
//那我就可以逆着来,看看哪些边界的水可以倒着流向比较高的位置
//到了最后两个洋重复的相交的区域就是我们想要的区域
height = heights.length;
wide = heights[0].length;
isFloodPacific = new boolean[height][wide];
isFloodAtlantic = new boolean[height][wide];
//扫描边界,先处理太平洋
for(int i = 0;i < height;i++){
for(int j = 0;j < wide;j++){
if(i == 0 && !isFloodPacific[i][j]){
isFloodPacific[i][j] = true;
dfs(heights,i,j,isFloodPacific);
}
if(j == 0 && !isFloodPacific[i][j]){
isFloodPacific[i][j] = true;
dfs(heights,i,j,isFloodPacific);
}
}
}
//扫描边界,再处理大西洋
for(int i = 0;i < height;i++){
for(int j = 0;j < wide;j++){
if(i == height-1 && !isFloodAtlantic[i][j]){
isFloodAtlantic[i][j] = true;
dfs(heights,i,j,isFloodAtlantic);
}
if(j == wide-1 && !isFloodAtlantic[i][j]){
isFloodAtlantic[i][j] = true;
dfs(heights,i,j,isFloodAtlantic);
}
}
}
//统计结果
List<List<Integer>> list = new ArrayList<>();
//提取两个标记数组重复的部分
for(int i = 0;i < height;i++){
for(int j = 0;j < wide;j++){
if(isFloodPacific[i][j] && isFloodAtlantic[i][j]){
List<Integer> tmp = new ArrayList<>();
tmp.add(i);
tmp.add(j);
list.add(tmp);
}
}
}
return list;
}
//同理使用向量数组
int [] x = {0,0,-1,1};
int [] y = {1,-1,0,0};
//同理posx表示行,posy表示列
private void dfs(int [][] heights,int posx,int posy,boolean [][] isFlood){
//根据参数会自动辨别当前判断是哪个洋
for(int i = 0;i < 4;i++){
int curX = posx+x[i];
int curY = posy+y[i];
if(curX >= 0 && curX < height && curY >= 0 && curY < wide && !isFlood[curX][curY]){
if(heights[curX][curY] >= heights[posx][posy]){
//代表此地可以往下流
isFlood[curX][curY] = true;
dfs(heights, curX, curY, isFlood);
}
}
}
}
}
六、扫雷游戏
这题的意思就是,如果我们一开始就挖出地雷,游戏结束,把这个位置修改为X
如果没有一开始就踩雷,我们就去扫描周边区域
如果存在地雷,就要把地雷数量显示到当前位置 ,然后不能往周围再展开,要回溯到上一个区域(如果直接展开,那我就没必要排雷了)
我们之前写向量数组都是四个方向,现在变成八个方向了,这个需要注意
java
class Solution {
int height;
int wide;
public char[][] updateBoard(char[][] board, int[] click) {
height = board.length;
wide = board[0].length;
//判断是否是直接点击了地雷
int clickX = click[0];
int clickY = click[1];
if(board[clickX][clickY] == 'M'){
//游戏直接结束
board[clickX][clickY] = 'X';
return board;
}
//接下来就不是直接点击了地雷的情况
dfs(board,click[0],click[1]);
return board;
}
//使用向量数组
int [] x = {0,0,1,-1,1,1,-1,-1};
int [] y = {1,-1,0,0,1,-1,1,-1};
//同理posx表示行,posy表示列
private void dfs(char [][] board,int posx,int posy){
int countM = 0;
for(int i = 0;i < 8;i++){
int curX = posx+x[i];
int curY = posy+y[i];
//遍历周边区域,看看有没有地雷
if(curX >= 0 && curX < height && curY >= 0 && curY < wide && board[curX][curY] == 'M'){
countM++;
}
}
if(countM > 0){
//说明当前区域存在地雷,修改当前位置值之后,回溯
board[posx][posy] = Character.forDigit(countM, 10);
return;
}
//就递归地展开周边区域
board[posx][posy] = 'B';
for(int i = 0;i < 8;i++){
int curX = posx+x[i];
int curY = posy+y[i];
if(curX >= 0 && curX < height && curY >= 0 && curY < wide && board[curX][curY] == 'E'){
dfs(board, posx+x[i], posy+y[i]);
}
}
}
}
七、衣橱整理
这题的意思就是把坐标中各个数位提取出来,如果它们的和大于题目给的目标值,我们就不能进入这个区域
反之,如果数位的和小于目标值,则表示可以进入
比如对于坐标[35,37]区域,数位和3+5+3+7 = 18 <= target = 18,可以进入这个区域
反之比如坐标[35,38]区域,数位和3+5+3+8 = 19 > target = 18,不可以进入这个区域,进行回溯
java
class Solution {
int height;
int wide;
//统计结果
int ret = 1;
//全局变量目标值
int target;
//标记已经递归过的格子
boolean [][] isUse;
public int wardrobeFinishing(int m, int n, int cnt) {
height = m;
wide = n;
target = cnt;
isUse = new boolean[height][wide];
isUse[0][0] = true;
dfs(0,0);
return ret;
}
//同理使用向量数组
int [] x = {0,1};
int [] y = {1,0};
//同理posx表示行,posy表示列
private void dfs(int posx,int posy){
for(int i = 0;i < 2;i++){
int curX = posx+x[i];
int curY = posy+y[i];
if(curX >= 0 && curX < height && curY >= 0 && curY < wide && !isUse[curX][curY]){
//统计数位
int tmpx = curX;
int tmpy = curY;
int count = 0;
while(tmpx > 0){
count += tmpx % 10;
tmpx /= 10;
}
while(tmpy > 0){
count += tmpy % 10;
tmpy /= 10;
}
//进行判断
if(count > target){
//不可以进行递归,寻找另一个方向
continue;
}
//到这里就说明可以进行递归
isUse[curX][curY] = true;
ret++;
dfs(curX, curY);
}
}
}
}
感谢您的阅读,如果您有更好的建议,欢迎指出
END