当前位置: 首页 > news >正文

网格图--Day02--网格图DFS--面试题 16.19. 水域大小,LCS 03. 主题空间,463. 岛屿的周长

网格图–Day02–网格图DFS–面试题 16.19. 水域大小,LCS 03. 主题空间,463. 岛屿的周长

今天要训练的题目类型是:【网格图DFS】,题单来自@灵艾山茶府。

适用于需要计算连通块个数、大小的题目。

部分题目做法不止一种,也可以用 BFS 或并查集解决。

DFS函数中的三步曲:判断,处理,继续DFS

  • 判断:是否越界,是否是需要DFS的格子
  • 处理:根据题意处理格子
  • 继续DFS:DFS四个方向,有时候可能需要收集返回值。

面试题 16.19. 水域大小

思路:

这道题跟695. 岛屿的最大面积是一样的,只不过一个是求岛屿的面积,一个是求水的面积,逻辑完全一样。

区别:

  • 这题要求八个方向。
  • 要返回每个面积,而不是最大面积。
class Solution {// 八个方向private final int[][] DIRS = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },{ 1, 1 }, { -1, -1 }, { 1, -1 }, { -1, 1 } };private int dfs(int[][] land, int i, int j) {if (i < 0 || j < 0 || i >= land.length || j >= land[0].length || land[i][j] != 0) {return 0;}// 标记为已访问(标记为陆地)land[i][j] = 1;// 当前格子水域大小为1int area = 1;for (int k = 0; k < DIRS.length; k++) {int x = i + DIRS[k][0];int y = j + DIRS[k][1];// 注意这里是加等于。area += dfs(land, x, y);}return area;}public int[] pondSizes(int[][] land) {int n = land.length;int m = land[0].length;List<Integer> list = new ArrayList<>();for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (land[i][j] == 0) {int area = dfs(land, i, j);list.add(area);}}}// 要排序后返回int[] res = list.stream().mapToInt(Integer::intValue).toArray();Arrays.sort(res);return res;}
}

这里扩展一下将List转为int[]的方法:

// 手动,使用for循环(效率最高,因为不用创建流)
int[] arr = new int[list.size()];
int i = 0;
for (int x : list)arr[i++] = x;
Arrays.sort(arr);// 使用stream转,然后排序
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
Arrays.sort(arr);// 使用stream,一句话转完并排序
int[] arr =list.stream().sorted().mapToInt(Integer::intValue).toArray();// List<int[]> 转为int[][]
int[][] array = list.toArray(int[][]::new);// List<List<Integer>> 转为int[][]
int[][] array = list.stream().map(row -> row.stream().mapToInt(Integer::intValue).toArray()).toArray(int[][]::new);
// ps:这段代码可能会产生每行长度不同的 int[][] 数组。
// 原因是:代码会严格按照原 List<List<Integer>> 中每个子列表(row)的实际长度来创建对应的 int[] 数组。

LCS 03. 主题空间

思路【我】:

总的思路:全局布尔变量connect标记,是否与水(走廊)连接。

  • 对于主函数
    • 遍历不是水,且没访问过的格子。
    • 从主函数进去是一个新岛,每次都要刷新,默认为false
    • 如果DFS完出来,还是false的话,表明它没有与水连接,更新res。
  • 对于每一个DFS,
    • 如果越到水(格子里的水,或者海水(越界了)),connect赋值true,返回。
    • 如果不是相同元素,返回。
    • 统计面积。
    • 踩坑点:对于下一层DFS,不要传grid[i][j],已经被修改过了,要传same
class Solution {private final int[][] DIRS = { { 0, 1 }, { 1, 0 }, { -1, 0 }, { 0, -1 } };private boolean connect = false;private int dfs(char[][] grid, int i, int j, char same) {// 如果越到水(格子里的水,或者海水(越界了)),connect为true,返回if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') {connect = true;return 0;}// 如果不是相同元素,返回。if (grid[i][j] != same) {return 0;}// 标记为已访问grid[i][j] = 'x';// 统计面积int area = 1;for (int k = 0; k < DIRS.length; k++) {int x = i + DIRS[k][0];int y = j + DIRS[k][1];// 踩坑点,这里不要传grid[i][j],已经被修改过了,要传samearea += dfs(grid, x, y, same);}return area;}public int largestArea(String[] grid) {int n = grid.length;int m = grid[0].length();// 转为char[]数组char[][] g = new char[n][m];int p = 0;for (String s : grid) {g[p++] = s.toCharArray();}int res = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {// 遍历不是水,且没访问过的格子if (g[i][j] != '0' && g[i][j] != 'x') {// 从这里进去是一个新岛,每次都要刷新,默认为falseconnect = false;int area = dfs(g, i, j, g[i][j]);// 如果不与水连接,那么更新resif (!connect) {res = Math.max(res, area);}}}}return res;}
}

思路:

在递归时,把connect状态,面积,包装为int[]数组返回给上一层

返回值设为长度为 2 的int[]数组。[0]表示面积,[1]表示是否与水相连。

[1]只有两个状态,0不相连,1相连

class Solution {private final int[][] DIRS = { { 0, 1 }, { 1, 0 }, { -1, 0 }, { 0, -1 } };// 返回值要有两个,[0]表示面积,[1]表示是否与走廊(水)相连,[1]只有两个状态,0不相连,1相连private int[] dfs(char[][] grid, int i, int j, char same) {// 如果越到水(格子里的水,或者海水(越界了)),connect为true,返回if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') {return new int[] { 0, 1 };}// 如果不是相同元素,返回。if (grid[i][j] != same) {return new int[] { 0, 0 };}// 标记为已访问grid[i][j] = 'x';// 统计面积int[] res = new int[2];res[0] = 1;for (int k = 0; k < DIRS.length; k++) {int x = i + DIRS[k][0];int y = j + DIRS[k][1];// 踩坑点,这里不要传grid[i][j],已经被修改过了,要传sameint[] cur = dfs(grid, x, y, same);res[0] += cur[0];// 如果传来标记为遇到水,要向上传递。if (cur[1] == 1) {res[1] = 1;}}return res;}public int largestArea(String[] grid) {int n = grid.length;int m = grid[0].length();// 转为char[]数组char[][] g = new char[n][m];int p = 0;for (String s : grid) {g[p++] = s.toCharArray();}int res = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {// 遍历不是水,且没访问过的格子if (g[i][j] != '0' && g[i][j] != 'x') {int[] cur = dfs(g, i, j, g[i][j]);if (cur[1] == 0) {res = Math.max(res, cur[0]);}}}}return res;}
}

463. 岛屿的周长

提前看:

思路一:探索每个节点,判断它的上下左右是否为水,返回周长。

思路二:探索每个节点,统计节点个数,与节点的接触的边的数量,每接触一条边,就会损失2的周长。最终结果等于:节点*4 - 接触边*2

思路三:直接迭代算。不用DFS

思路一:

  • 探索每个节点,判断它的上下左右是否为水,返回周长。

注意!标记节点为已访问状态,不能grid[i][j] = 0;,否则会影响。

class Solution {private final int[][] DIRS = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };private int dfs(int[][] grid, int i, int j) {// 标记已访问grid[i][j] = 2;int count = 0;// 处理本节点,当触碰到边界(边界是水),或者格子是水的时候,边长加一// 上下左右if (i - 1 < 0 || grid[i - 1][j] == 0) {count++;}if (i + 1 >= grid.length || grid[i + 1][j] == 0) {count++;}if (j - 1 < 0 || grid[i][j - 1] == 0) {count++;}if (j + 1 >= grid[0].length || grid[i][j + 1] == 0) {count++;}// 下一个节点for (int k = 0; k < DIRS.length; k++) {int x = i + DIRS[k][0];int y = j + DIRS[k][1];// x,y合法,且格子为陆地if (!(x < 0 || y < 0 || x >= grid.length || y >= grid[0].length) && grid[x][y] == 1) {count += dfs(grid, x, y);}}return count;}public int islandPerimeter(int[][] grid) {int n = grid.length;int m = grid[0].length;int res = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (grid[i][j] == 1) {res += dfs(grid, i, j);}}}return res;}
}

思路二:

  • 探索每个节点,统计节点个数,与节点的接触的边的数量。
  • 每接触一条边,就会损失2的周长。
  • 最终结果等于:节点*4 - 接触边*2

注意!在判断邻居是不是陆地的时候,不能用1来判断,因为有的陆地已经被标记为2了。

class Solution {private final int[][] DIRS = { { 0, 1 }, { 1, 0 }, { -1, 0 }, { 0, -1 } };private int touch = 0;private int dfs(int[][] grid, int i, int j) {// 标记为已访问grid[i][j] = 2;// 右,下(不是水,就++);不能用1来判断,因为有的陆地已经被标记为2了if (j + 1 < grid[0].length && grid[i][j + 1] != 0) {touch++;}if (i + 1 < grid.length && grid[i + 1][j] != 0) {touch++;}// 统计面积int area = 1;for (int k = 0; k < DIRS.length; k++) {int x = i + DIRS[k][0];int y = j + DIRS[k][1];// xy合法,访问下一个没有访问过的陆地if (!(x < 0 || y < 0 || x >= grid.length || y >= grid[0].length) && grid[x][y] == 1) {area += dfs(grid, x, y);}}return area;}public int islandPerimeter(int[][] grid) {int n = grid.length;int m = grid[0].length;int land = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (grid[i][j] == 1) {land += dfs(grid, i, j);}}}return land * 4 - touch * 2;}
}

思路三:

其实不用DFS,直接迭代,统计每个陆地格子的情况就行了。

class Solution {public int islandPerimeter(int[][] grid) {int n = grid.length;int m = grid[0].length;int count = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (grid[i][j] == 0) {continue;}// 如果i在上边界,或者格子是水。(下同理)if (i == 0 || grid[i - 1][j] == 0) {count++;}if (i == n - 1 || grid[i + 1][j] == 0) {count++;}if (j == 0 || grid[i][j - 1] == 0) {count++;}if (j == m - 1 || grid[i][j + 1] == 0) {count++;}}}return count;}
}
http://www.dtcms.com/a/360418.html

相关文章:

  • 技术总体方案设计思路
  • SAP报工与收货的区别(来自deepseek)
  • c++ 二维码、条形码开发实例
  • FFMPEG学习任务
  • 为什么计算机使用补码存储整数:补码的本质
  • 自定义AXI_PWM_v1.0——ZYNQ学习笔记15
  • Ultra Low Power Transceiver for Wireless Body Area Networks中文版
  • Makefile语句解析:头文件目录自动发现与包含标志生成
  • Day 01(01): Hadoop与大数据基石
  • RPC个人笔记(包含动态代理)
  • Qwen2.5-VL代码初步解读
  • 一个从7zip中分离出来的高压缩比文本压缩工具ppmd
  • 使用PowerShell监听本地端口
  • 多线程案例、线程池
  • QT6(QStandardItemModel和QTableView及自定义代理)
  • 第3章 乱码的前世今生-字符集和比较规则
  • 部署在windows的docker中的dify知识库存储位置
  • 常见线程池的创建方式及应用场景
  • Cookie、Session 和 JWT
  • 【K8s-Day 22】深入解析 Kubernetes Deployment:现代应用部署的基石与滚动更新的艺术
  • 服装管理软件与工厂计件系统精选
  • 【OpenGL】LearnOpenGL学习笔记18 - Uniform缓冲对象UBO
  • [每周一更]-(第158期):构建高性能数据库:MySQL 与 PostgreSQL 系统化问题管理与优化指南
  • XPlayer播放器APP:安卓平台上的全能视频播放器
  • 网络代理协议深度对比
  • Linux/UNIX系统编程手册笔记:系统和进程信息、文件I/O缓冲、系统编程概念以及文件属性
  • Multi-Head RAG: Solving Multi-Aspect Problems with LLMs
  • ST-2110概述
  • MySQL专题Day(1)————事务
  • postman 用于接口测试,举例