Hot 100 --- 二叉树的最近公共祖先
本文概览本文以LeetCode题目二叉树的最近公共祖先为例讲解后序遍历回溯汇总的思路重点说明三种返回值情况的处理一、题目二、题目分析题目要求给定二叉树根节点root以及两个节点p和q找到它们的最近公共祖先最近公共祖先的定义设节点root为节点p、q的某公共祖先若其左子节点root.left和右子节点root.right都不是p、q的公共祖先则称root是最近的公共祖先根据这个定义判断一个节点是不是最近公共祖先就要看它的左右子节点是不是公共祖先——如果左右子节点都不是公共祖先那当前节点就是最近公共祖先。最典型的情况就是p和q分别位于当前节点的左右两侧3 / \ 5 1 ← 3 是最近公共祖先p5 在左q1 在右 / \ \ 6 2 8所以核心思路就是后序遍历 回溯汇总。后序遍历先看左右子树再把左右子树的结果汇总到当前节点做判断思路概览Java 实现代码如下publicTreeNodelowestCommonAncestor(TreeNoderoot,TreeNodep,TreeNodeq){returndfs(root,p,q);}privateTreeNodedfs(TreeNodenode,TreeNodep,TreeNodeq){// 如果当前节点为空返回nullif(nodenull){returnnull;}// 如果当前节点是p或q返回当前节点if(nodep||nodeq){returnnode;}// 递归搜索左子树TreeNodeleftdfs(node.left,p,q);// 递归搜索右子树TreeNoderightdfs(node.right,p,q);// 如果左子树和右子树都返回了非null值说明当前节点是最近公共祖先if(left!nullright!null){returnnode;}// 如果左子树或右子树返回了非null值说明最近公共祖先在该子树中returnleft!null?left:right;}思路简要说明整体是后序遍历 回溯汇总递归出口当前节点为空返回 null当前节点就是p或q直接返回自身后序遍历先递归左子树、再递归右子树拿到left和right两个返回值三种情况汇总left和right都不为 null →p、q分别在两侧当前节点就是最近公共祖先left和right只有一个不为 null → 把这个非 null 的值往上返回让上层节点继续判断left和right都为 null → 当前子树没找到返回 null核心就是每一步都把子树里找到了什么往上传让上层节点做判断三、思路详解第一步为什么是后序遍历要判断一个节点是不是最近公共祖先必须先知道它的左子树和右子树里有没有p和q。也就是说先处理左右子树再处理当前节点——这正是后序遍历左→右→根的顺序3 / \ 5 1 后序遍历顺序5 → 1 → 3 遍历到 3 时已经知道左子树找到了 5右子树找到了 1 → 3 就是最近公共祖先如果是前序遍历根→左→右到了 3 还没遍历左右子树根本不知道下面有没有p、q没法判断第二步递归的两个出口递归函数dfs(node, p, q)的作用是在以node为根的子树中查找p和q返回找到的节点或最近公共祖先出口 1node null遍历到空节点说明走到底了没找到返回 null出口 2node p或node q当前节点本身就是p或q直接返回自身。这里有一个关键点一旦命中就直接返回不再往下递归为什么不往下递归因为p或q已经找到了它下面的子树再找也没意义。另一个节点只可能有两种位置在它的子树里那p或q自己就是最近公共祖先祖先可以包含自己不在它的子树里那当前节点只是一个普通的目标节点上层的其他分支会找到另一个最后由上层汇总判断不管哪种情况当前节点只需要把自己返回给父节点就够了不需要往下递归第三步左右子树返回值的三种情况递归完左右子树后拿到left和right两个返回值。这两个值有三种组合每种对应一种情况情况 1left ! null right ! null两边都不为空说明左子树找到了一个p或q右子树也找到了另一个。此时当前节点就是最近公共祖先——p和q分别在它的左右两侧3 / \ 5 1 ← left5, right13 是最近公共祖先 / \ \ 6 2 8返回当前节点node情况 2left和right只有一个不为 null此时有两种子情况但对代码来说处理方式完全一样子情况 A最近公共祖先就在这个非 null 的子树里现在还没走到那一步需要把这个非 null 的值继续往上传递让上层节点去判断子情况 B找到的就是p或q本身另一个节点在它的子树下面所以p或q自己就是最近公共祖先不管是哪种子情况处理方式都是把非 null 的那个值往上返回子情况A示例最近祖先在子树深处往上传递 3 / \ 5 null ← 5 子树里找到了 p、q最近祖先是 5 / \ 6 2 ← 5 的 left6 不为nullright2 不为null → 5 是最近祖先 ← 3 的 left5返回的最近祖先rightnull → 把 5 往上传 子情况B示例p 或 q 自己就是最近祖先 3 / \ 5 1 / \ 6 2 ← p5, q2q 在 p 的子树里 ← 遍历到 5 时直接命中 p返回 5 ← 3 的 left5rightnull → 把 5 往上传5 就是最近祖先情况 3left和right都为 null说明左右子树都没找到p或q当前节点的子树里没有目标返回 nullreturnleft!null?left:right;// 如果 left 不为 null 返回 left否则返回 right// left 和 right 都为 null 时返回 right也是 null// left 和 right 只有一个不为 null 时返回那个非 null 的// left 和 right 都不为 null 时上面已经 return 了走不到这里这一行代码同时处理了情况 2 和情况 3很简洁第四步完整执行过程以这棵树为例3 / \ 5 1 / \ \ 6 2 8下面用三个例子分别演示三种情况。核心要盯住每个节点递归后拿到的left和right——左右子树返回了什么决定了当前节点怎么处理例1p 5q 1p、q 分别在根的左右两侧初始从根节点 3 开始访问节点 3当前路径3不是 p 也不是 q递归左右子树访问节点 5当前路径3→5命中 p5直接返回 5不再往下递归→ left 5访问节点 1当前路径3→1命中 q1直接返回 1不再往下递归→ right 1回到节点 3left5 不为 nullright1 不为 null → 3 就是最近公共祖先返回 3结果最近公共祖先是 3例2p 5q 2q 在 p 的子树里访问节点 3当前路径3不是 p 也不是 q递归左右子树访问节点 5当前路径3→5命中 p5直接返回 5不再往下递归2 虽然在 5 的子树里但命中后不往下找→ left 5访问节点 1当前路径3→1不是 p 也不是 q递归左右子树访问节点 null1 的左子树空节点返回 null→ left null访问节点 8当前路径3→1→8不是 p 也不是 q左右子树都是 null返回 null→ right null回到节点 1leftnullrightnull → 返回 null回到节点 3left5 不为 nullrightnull → 把 5 往上传返回 5结果最近公共祖先是 5q2 在 p5 的子树里p 自己就是最近祖先例3p 6q 2都在左子树最近祖先在深处访问节点 3当前路径3不是 p 也不是 q递归左右子树访问节点 5当前路径3→5不是 p 也不是 q递归左右子树访问节点 6当前路径3→5→6命中 p6直接返回 6→ left 6访问节点 2当前路径3→5→2命中 q2直接返回 2→ right 2回到节点 5left6 不为 nullright2 不为 null → 5 就是最近公共祖先返回 5→ 节点 3 的 left 5访问节点 1当前路径3→1不是 p 也不是 q递归左右子树左子树 null右子树 8 也不是 p、q → leftnullrightnull → 返回 null→ 节点 3 的 right null回到节点 3left5 不为 nullrightnull → 把 5 往上传返回 5结果最近公共祖先是 56 和 2 分别在 5 的左右两侧三个例子的共性例1左右子树都返回非 null → 当前节点就是最近祖先例2、例3只有一边返回非 null → 把这个非 null 的值往上传递最终传到根节点的就是答案不管最近祖先在哪个位置它一定是第一次出现 left 和 right 都不为 null的那个节点找到后就会一路被往上传第五步回溯汇总的本质整个过程其实就是回溯汇总每个节点把左右子树的查找结果汇总到一起做一次判断然后把结果往上传左右都找到了 → 当前节点就是最近祖先把自己往上返回只有一边找到了 → 把那一边的结果往上返回让上层继续判断两边都没找到 → 返回 null告诉上层这里没有最终结果会一层一层传递回根节点根节点拿到的就是最终答案这种后序遍历先拿到子树结果再在当前节点汇总的模式是二叉树问题中很常见的一种思路适用于需要综合左右子树信息来做判断的场景复杂度分析时间复杂度O(n)每个节点最多遍历一次空间复杂度O(h)递归栈深度等于树的高度最坏情况 O(n)

相关新闻

WAIC首个AI影视专场落幕,三个信号值得创作者关注

WAIC首个AI影视专场落幕,三个信号值得创作者关注

今天,WAIC 2026首个AI影视专场论坛在上海世博展览馆落幕。 这场名为"世界模型驱动下的AI影视生产力新范式"的论坛,由万兴科技和生数科技联合承办,集结了AI基础设施、大模型、创作工具到影视内容方的全链路嘉宾。九届WAIC第一次单独…

2026/7/22 8:32:19 阅读更多 →
大家都在聊Hermes,企业真正需要的却不是更多 Demo

大家都在聊Hermes,企业真正需要的却不是更多 Demo

聊《大家都在聊Hermes,企业真正需要的却不是更多 Demo》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。 摘要 先把这篇文章的目标说清楚:看完之后,你应该能判断这件事值不值得做…

2026/7/22 5:36:39 阅读更多 →
Spring Boot 3.0 深度实战:核心特性剖析与生产级调优指南

Spring Boot 3.0 深度实战:核心特性剖析与生产级调优指南

1. 引言:为什么选择 Spring Boot 3.0?Spring Boot 3.0 是 Spring 框架发展历程中的一个重要里程碑,它不仅基于 Spring Framework 6.0 和 Java 17 构建,更在性能、开发体验和云原生支持上带来了质的飞跃。对于开发者而言&#xff0…

2026/7/21 0:41:40 阅读更多 →

最新新闻

TRELLIS.2:3秒照片转3D模型的技术突破

TRELLIS.2:3秒照片转3D模型的技术突破

1. 项目概述:TRELLIS.2如何颠覆3D生成领域 微软亚洲研究院最新发布的TRELLIS.2技术,彻底改变了传统3D模型生成的游戏规则。这项技术能够在短短3秒内将普通照片转换为高保真3D模型,其核心突破在于解决了当前3D生成领域的三大痛点:材…

2026/7/22 8:31:58 阅读更多 →
让 AI 第一次拥有行动能力

让 AI 第一次拥有行动能力

因此,对于模型来说,整个世界只有两件事情: 输入文本 ↓ 生成文本 如果只是聊天、翻译、总结、写代码,这已经足够了。 但是,当我们开始尝试让 AI 帮助完成真实工作时,很快就会遇到一个问题 当我们问模型: 帮…

2026/7/22 8:31:58 阅读更多 →
实战解析:如何绕过WAF并逆向WASM加密实现深度渗透测试

实战解析:如何绕过WAF并逆向WASM加密实现深度渗透测试

1. 项目概述:一次针对WAF与WASM加密的实战渗透 最近在做一个授权范围内的安全测试项目,目标是一个金融类Web应用。在初步信息收集和常规漏洞扫描阶段,我遇到了一个典型的“现代防御组合拳”:Web应用防火墙(WAF&#xf…

2026/7/22 8:31:58 阅读更多 →
iOS性能优化利器:Instruments工具全解析

iOS性能优化利器:Instruments工具全解析

1. iOS性能优化与Instruments工具概述在iOS应用开发中,性能优化是确保用户体验的关键环节。一个响应迟缓、内存占用过高或耗电量大的应用,即使用户界面再精美,也难逃被卸载的命运。作为苹果官方提供的性能分析工具套件,Instrument…

2026/7/22 8:31:58 阅读更多 →
征兵管理系统技术实现:微服务架构、数据安全与高并发设计

征兵管理系统技术实现:微服务架构、数据安全与高并发设计

这次我们来看一个与征兵相关的技术项目。虽然"征兵"本身是一个政策性和社会性话题,但在技术领域,我们可以从多个角度来探讨相关的信息化建设、数据处理和系统开发方案。本文将重点分析征兵信息化系统的技术实现方案,包括系统架构、…

2026/7/22 8:31:58 阅读更多 →
JavaScript TypeError: undefined is not a function 解析与解决方案

JavaScript TypeError: undefined is not a function 解析与解决方案

1. 错误现象与本质剖析 "TypeError: undefined is not a function"这个报错在JavaScript开发中出现的频率堪比咖啡杯旁的糖包。当你在控制台看到这行红字时,本质上是在说:"你试图把不存在的东西当函数调用"。就像对着空气喊"开…

2026/7/22 8:30:58 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/21 8:48:31 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 5:34:47 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/21 8:25:39 阅读更多 →

月新闻