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

LeetCode 双杀!二叉树最大路径和 + 岛屿数量|DFS 两大经典模板题

前言

DFS(深度优先搜索)是算法面试的核心技能,它不仅适用于二叉树,也适用于图论问题。今天这两道题,一道是二叉树的 DFS 经典难题,另一道是图论的 DFS 入门模板题。它们的核心思想一脉相承,但在应用场景上各有侧重。

这篇博客会带你彻底搞懂它们的解题思路、代码实现和背后的通用模板,看完就能直接上手!


一、二叉树中的最大路径和(困难)

🎯 题目描述

给定一个非空二叉树,返回其最大路径和。路径被定义为一条从树中任意节点出发,沿父节点 - 子节点连接,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。

💡 核心思路(后序 DFS)

这道题的关键在于理解 “路径” 的定义:

  • 路径可以从任意节点出发,到任意节点结束,但不能分叉
  • 也就是说,在路径中,只能有一个节点作为 “拐点”(路径的最高点)。

我们的解法是:

  1. 使用后序遍历:先计算左右子树的贡献,再处理当前节点。
  2. 定义递归函数dfs(node):返回以node为起点,向其子树延伸的最大路径和(只能选左或右,不能同时选)。
  3. 更新全局最大值:在递归过程中,计算以当前节点为 “拐点” 的路径和(node.val + leftContribution + rightContribution),并更新全局最大值。

✅ 完整 AC 代码

java

运行

/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { // 全局变量记录最大路径和,初始值设为负无穷 private int maxSum = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) { dfs(root); return maxSum; } /** * 返回以 node 为起点,向其子树延伸的最大路径和 * 同时更新全局最大路径和 */ private int dfs(TreeNode node) { if (node == null) { return 0; } // 递归计算左右子树的贡献,若为负则取0(不选) int leftContribution = Math.max(dfs(node.left), 0); int rightContribution = Math.max(dfs(node.right), 0); // 以当前节点为拐点的路径和,更新全局最大值 int currentPathSum = node.val + leftContribution + rightContribution; maxSum = Math.max(maxSum, currentPathSum); // 返回当前节点向父节点的贡献(只能选左或右) return node.val + Math.max(leftContribution, rightContribution); } }

📝 关键细节解析

  1. 为什么要取Math.max(dfs(...), 0)如果子树的贡献是负数,那么选择不经过这个子树(贡献为 0)会更好。
  2. currentPathSumreturn的区别?
    • currentPathSum以当前节点为拐点的路径和,可以同时包含左右子树,用来更新全局最大值。
    • return的值是向父节点传递的贡献,只能包含当前节点 + 左 / 右子树中的一条路径,因为路径不能分叉。

二、岛屿数量(中等)

🎯 题目描述

给你一个由'1'(陆地)和'0'(水)组成的二维网格,计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和 / 或竖直方向上相邻的陆地连接形成。

💡 核心思路(图的 DFS / Flood Fill)

这道题是图的 DFS入门模板题,核心思想是 “染色”:

  1. 遍历整个网格:遇到未访问的陆地('1'),说明发现了一个新岛屿。
  2. 启动 DFS:从这个陆地出发,向上下左右四个方向递归搜索。
  3. 标记已访问:在 DFS 过程中,将访问过的陆地标记为'0'(水),避免重复计算。
  4. 岛屿计数:每启动一次 DFS,岛屿数量 +1。

✅ 完整 AC 代码

java

运行

class Solution { public int numIslands(char[][] grid) { if (grid == null || grid.length == 0) { return 0; } int count = 0; int rows = grid.length; int cols = grid[0].length; // 遍历整个网格 for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { // 遇到未访问的陆地,岛屿数量+1,并启动DFS染色 if (grid[i][j] == '1') { count++; dfs(grid, i, j, rows, cols); } } } return count; } /** * DFS染色:将(i,j)及其连通的陆地标记为'0' */ private void dfs(char[][] grid, int i, int j, int rows, int cols) { // 越界判断,或遇到水/已访问的陆地,直接返回 if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] == '0') { return; } // 标记为已访问(染色) grid[i][j] = '0'; // 向上下左右四个方向递归 dfs(grid, i - 1, j, rows, cols); // 上 dfs(grid, i + 1, j, rows, cols); // 下 dfs(grid, i, j - 1, rows, cols); // 左 dfs(grid, i, j + 1, rows, cols); // 右 } }

📝 关键细节解析

  1. 染色法:直接修改原数组,将访问过的'1'改为'0',无需额外空间记录访问状态,空间复杂度为 O (1)(不算递归栈)。
  2. 边界判断:在 DFS 入口处统一判断越界或遇到水,逻辑更清晰。
  3. 时间复杂度:每个单元格最多被访问一次,时间复杂度为 O (M*N),其中 M、N 为网格的行数和列数。

🎯 两道题核心思想对比

表格

题目数据结构DFS 类型核心技巧
最大路径和二叉树后序 DFS全局变量记录最大值,区分 “拐点路径和” 与 “向父节点的贡献”
岛屿数量二维网格(图)Flood Fill DFS染色法标记已访问,避免重复计算连通分量

结语

这两道题是 DFS 思想的绝佳范例:

  • 最大路径和教你如何利用后序遍历,在递归过程中同时更新全局状态。
  • 岛屿数量教你如何将 DFS 应用到图论问题中,解决连通分量计数。
http://www.jsqmd.com/news/594034/

相关文章:

  • W5500 TCP客户端实战 | 02 - 从寄存器配置到数据收发的完整流程解析
  • 基于FPGA的LMS自适应滤波器设计与实现(Verilog代码及仿真)
  • 2025届学术党必备的六大降重复率神器横评
  • TCP 和 UDP 有什么区别:从可靠性到速度,从头部到场景
  • BFS 经典双题:腐烂的橘子 + 课程表 | 拓扑排序入门必刷
  • 别再只调参了!深入torchvision.datasets.CIFAR10源码,理解PyTorch数据加载的设计哲学
  • 2025届学术党必备的六大AI论文助手解析与推荐
  • Ubuntu 22.04下Milvus集群部署实战:从Docker提取二进制文件的完整指南
  • 基于Simulink的四自由度磁悬浮轴承控制仿真:电流环、位置环、位移解析及磁轴承模型PID控...
  • DSI3协议四大模式(CRM/PDCM/BDM/DM)全解析:从汽车胎压监测到电池管理,看它如何工作
  • 1Panel面板深度体验:比宝塔更轻量的Docker管理方案?CasaOS环境实测对比
  • 为什么要 TCP,IP 层实现控制不行么:从分层哲学到不可逾越的物理限制
  • 2026-4-5
  • Python办公自动化:3种Word转PDF方法实测(附代码对比)
  • 前端必懂:开发环境、构建打包的核心差异,新手再也不踩坑
  • 深度学习检测不准确智能电表案例研究代码功能说明
  • “16QAM调制与解调系统的SystemView仿真及分析”
  • HJ164 太阳系DISCO
  • 手把手教你开发电竞护航系统:从零到上线的小程序全流程
  • 【Matlab 六自由度机器人】从理论到实践:笛卡尔与关节空间规划在复杂避障场景下的MATLAB实现与对比
  • 5个技巧让你高效畅玩Switch游戏:开源Ryujinx模拟器全攻略
  • 永磁同步电机(PMSM)速度电流双闭环FOC矢量控制策略详解
  • 解决GLIBC版本冲突:手动编译libcrypto.so.1.0.0的完整指南
  • 保姆级教程:在CentOS 7.9上从源码编译安装nvtop 3.1.0(含CMake 3.29.7依赖安装)
  • 前端CSS精讲05:Grid网格布局——现代页面最强二维布局方案
  • 你的电脑配置,可能决定了Vivado升级时IP会不会“偷懒”:一次关于IP缓存与硬件资源的观察
  • Ubuntu 20.04忘记密码?5分钟搞定root和用户密码重置(附GRUB菜单截图)
  • Avalonia实战:5分钟搞定无边框窗口自定义(附拖拽功能完整代码)
  • 学生评教|高校评教|基于SpringBoot+vue高校学生评教系统 (源码+数据库+文档)
  • 离谱又惊艳!C++隐藏宝藏库numeric_range深度探索,竟藏着JS彩蛋和隐零点