文章目录
- [一. 力扣 [733. 图像渲染](https://leetcode.cn/problems/flood-fill/description/)](#一. 力扣 733. 图像渲染)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
- [二. 力扣 [200. 岛屿数量](https://leetcode.cn/problems/number-of-islands/description/)](#二. 力扣 200. 岛屿数量)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
- [三. 力扣 [695. 岛屿的最大面积](https://leetcode.cn/problems/max-area-of-island/description/)](#三. 力扣 695. 岛屿的最大面积)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
- [四. 力扣 [130. 被围绕的区域](https://leetcode.cn/problems/surrounded-regions/description/)](#四. 力扣 130. 被围绕的区域)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
- [五. 力扣 [417. 太平洋大西洋水流问题](https://leetcode.cn/problems/pacific-atlantic-water-flow/description/)](#五. 力扣 417. 太平洋大西洋水流问题)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
- [六. 力扣 [529. 扫雷游戏](https://leetcode.cn/problems/minesweeper/description/)](#六. 力扣 529. 扫雷游戏)
-
- [1. 题目解析](#1. 题目解析)
- [2. 题目解析](#2. 题目解析)
- [3. 代码](#3. 代码)
- [七. 力扣 [LCR 130. 衣橱整理](https://leetcode.cn/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/description/)](#七. 力扣 LCR 130. 衣橱整理)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
对于floodfill问题的解决, 我们在算法奇妙屋(十三)给了BFS的解决方式, 题目基本一样
一. 力扣 733. 图像渲染
1. 题目解析
给定一个坐标, 值为r, 从这个坐标开始向四个方位移动, 将值等于r的全部修改为color
2. 算法原理
这类题目相对简单, 这里直接给出
3. 代码
java
class Solution {
int c = 0;
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
int m, n, r;
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
c = color;
m = image.length;
n = image[0].length;
r = image[sr][sc];
image[sr][sc] = color;
dfs(image, sr, sc);
return image;
}
void dfs(int[][] image, int i, int j) {
if (image[i][j] == r) {
return;
}
for (int k = 0; k < 4; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == r) {
image[x][y] = c;
dfs(image, x, y);
}
}
}
}
二. 力扣 200. 岛屿数量
1. 题目解析
2. 算法原理
通过循环找到所有没被标记且数值为1的坐标, 然后丢入dfs中去标记, 每运行一次dfs就是一个岛屿
3. 代码
java
class Solution {
int ret = 0, m, n;
boolean[][] vis;
public int numIslands(char[][] grid) {
m = grid.length;
n = grid[0].length;
vis = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1' && !vis[i][j]) {
vis[i][j] = true;
dfs(grid, i, j);
ret++;
}
}
}
return ret;
}
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
void dfs(char[][] grid, int i, int j) {
for (int k = 0; k < 4; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == '1') {
vis[x][y] = true;
dfs(grid, x, y);
}
}
}
}
三. 力扣 695. 岛屿的最大面积
1. 题目解析
2. 算法原理
1.函数头->发现重复子问题
我们需要知道每一个岛屿的任意一个坐标, 然后从当前坐标向四周延伸
1.全局变量: 标记数组vis, 记录最终结果ret, 记录每一个岛屿的面积的sum, 向量数组
2.参数: 原数组grid, 岛屿坐标i, j
2.函数体->宏观解决单个子问题
每次递归都代表有新的岛屿被发现, 需要向四个方位延伸来计算面积sum
3.递归的出口(起始点)
循环结束即代表递归结束
3. 代码
java
class Solution {
int ret = 0, sum = 0, m, n;
boolean[][] vis;
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
public int maxAreaOfIsland(int[][] grid) {
m = grid.length;
n = grid[0].length;
vis = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1 && !vis[i][j]) {
dfs(grid, i, j);
ret = Math.max(ret, sum);
sum = 0;
}
}
}
return ret;
}
void dfs(int[][] grid, int i, int j) {
vis[i][j] = true;
sum++;
for (int k = 0; k < 4; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == 1) {
dfs(grid, x, y);
}
}
}
}
四. 力扣 130. 被围绕的区域
1. 题目解析
这道题我们换个理解方式, 意思就是在边缘位置, 以及与边缘位置相连的区域 'O' 都不变, 而在中间被包围的不和边界 'O' 相连的都替换为 'X'
2. 算法原理
因为解决floodfill问题的思想基本一致, 所以这里只给出关键信息, 这道题主要运用正难则反的思想, 先将与边缘位置相邻的 'O' 都替换为一个标记字符 * , 然后遍历整个数组, 将剩余的 'O' 替换为 'X' , 标记字符 * 改为 'O'
3. 代码
java
class Solution {
boolean[][] vis;
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
int m, n;
public void solve(char[][] board) {
m = board.length;
n = board[0].length;
vis = new boolean[m][n];
// 标记边界行
for (int i = 0; i < m; i++) {
if (board[i][0] == 'O' && !vis[i][0]) {
dfs(board, i, 0);
}
if (board[i][n - 1] == 'O' && !vis[i][n - 1]) {
dfs(board, i, n - 1);
}
}
// 标记边界列
for (int j = 0; j < n; j++) {
if (board[0][j] == 'O' && !vis[0][j]) {
dfs(board, 0, j);
}
if (board[m - 1][j] == 'O' && !vis[m - 1][j]) {
dfs(board, m - 1, j);
}
}
// 将所有边界区域'*'恢复, 并将'O'替换
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 dfs(char[][] board, int i, int j) {
vis[i][j] = true;
board[i][j] = '*';
for (int k = 0; k < 4; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && board[x][y] == 'O') {
dfs(board, x, y);
}
}
}
}
五. 力扣 417. 太平洋大西洋水流问题
1. 题目解析
这道题的最大难度就是读懂题目... 可以说力扣官方的翻译业务有待提升
题目的意思就是, 水往低处流, 边界的网格可以直接流入大海, 题目要求我们返回的是所有既可以流入太平洋又可以流入大西洋的坐标
2. 算法原理
这道题依旧采取正难则反的思想, 我们分别从太平洋和大西洋的边界出发, 让往高处流, 能从边界留到的高地, 也就可以反正可以从该高地流向边界, 最后太平洋和大西洋相交的点符合条件的位置(图中同时被红黑箭头指向的位置)
3. 代码
java
class Solution {
// po代表太平洋, ao代表大西洋
boolean[][] po, ao;
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
int m, n;
public List<List<Integer>> pacificAtlantic(int[][] heights) {
m = heights.length;
n = heights[0].length;
po = new boolean[m][n];
ao = new boolean[m][n];
// 处理第一列与最后一列
for (int i = 0; i < m; i++) {
dfs(heights, i, 0, po);
dfs(heights, i, n - 1, ao);
}
// 处理第一行与最后一行
for (int j = 0; j < n; j++) {
dfs(heights, 0, j, po);
dfs(heights, m - 1, j, ao);
}
// 收集结果
List<List<Integer>> ret = new ArrayList<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (po[i][j] && ao[i][j]) {
List<Integer> tmp = new ArrayList<>();
tmp.add(i);
tmp.add(j);
ret.add(tmp);
}
}
}
return ret;
}
void dfs(int[][] heights, int i, int j, boolean[][] vis) {
vis[i][j] = true;
for (int k = 0; k < 4; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && heights[x][y] >= heights[i][j]) {
dfs(heights, x, y, vis);
}
}
}
}
六. 力扣 529. 扫雷游戏
1. 题目解析
题目字多还长, 但玩过扫雷的话还是很好理解这道题的
2. 题目解析
1. 重要的是让我们求的是, 给定一个坐标后, 这个坐标周围八个方位如果没有雷, 就可以揭开这些方位的格子, 同时这些被揭开的格子又可以向外扩张
2. 注意: 当给定的坐标周围八个方位只要有一个方向为雷, 就需要结束扩张(递归), 并在该位置标注上周围雷的个数
3. 我们需要两个八个方位的循环, 一个用来判断周围是否有雷, 并统计雷的个数, 一个用来向八方扩张
3. 代码
java
class Solution {
int[] dx = {0, 0, -1, 1, 1, -1, -1, 1};
int[] dy = {-1, 1, 0, 0, 1, -1, 1, -1};
int m, n, sum;
public char[][] updateBoard(char[][] board, int[] click) {
if (board[click[0]][click[1]] == 'M') {
board[click[0]][click[1]] = 'X';
return board;
}
m = board.length;
n = board[0].length;
dfs(board, click[0], click[1]);
return board;
}
void dfs(char[][] board, int i, int j) {
// 周围有地雷, 该位置修改为sum后, 直接结束递归
if (hasMine(board, i, j)) {
board[i][j] = (char)(sum + '0');
return;
}
board[i][j] = 'B';
for (int k = 0; k < 8; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'E') {
dfs(board, x, y);
}
}
}
// 判断周围是否有地雷, 并统计雷的个数
boolean hasMine(char[][] board, int i, int j) {
boolean f = false;
sum = 0;
for (int k = 0; k < 8; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'M') {
sum++;
f = true;
}
}
return f;
}
}
七. 力扣 LCR 130. 衣橱整理
1. 题目解析
这道题相对来说比较好理解和简单, 这里我们只需要考虑两个方位的移动
2. 算法原理
根据指定的方位来进行dfs扩展
3. 代码
java
class Solution {
boolean[][] vis;
int ret = 0, m, n, c;
int[] dx = {0, 1};
int[] dy = {1, 0};
public int wardrobeFinishing(int mm, int nn, int cnt) {
m = mm;
n = nn;
c = cnt;
vis = new boolean[m][n];
dfs(0, 0);
return ret;
}
void dfs(int i, int j) {
vis[i][j] = true;
ret++;
for (int k = 0; k < 2; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && digit(x, y) <= c) {
dfs(x, y);
}
}
}
int digit(int i, int j) {
int sum = 0;
while (i > 0) {
sum += i % 10;
i /= 10;
}
while (j > 0) {
sum += j % 10;
j /= 10;
}
return sum;
}
}













