【LeetCode经典题解】搞定二叉树最近公共祖先:递归法+栈存路径法,附代码实现

【LeetCode经典题解】搞定二叉树最近公共祖先:递归法+栈存路径法,附代码实现
在这里插入图片描述
🎁个人主页:User_芊芊君子
🎉欢迎大家点赞👍评论📝收藏⭐文章
🔍系列专栏:Java.数据结构
在这里插入图片描述


在这里插入图片描述


【前言】

二叉树的最近公共祖先是数据结构中的经典问题,无论是算法面试还是实际开发都高频出现。本文将从问题本身出发,拆解递归法与栈存路径法两种核心思路的逻辑步骤,并附上完整代码实现,帮你快速掌握这一考点。

文章目录:

一、二叉树的最近共同祖先

在这里插入图片描述

二、思路分析

方法一:递归法

判空

  • 如果根节点为空,直接返回null
  • 如果当前节点是p或者q,那么这个节点就是最近公共祖先

递归

  • 分别遍历当前节点的左子树和右子树,找到p和q的最近公共祖先,存于TreeNode leftRet或者TreeNode rightRet

结果

  • 如果左右子树都找到,那么当前节点就是p和q的最近公共祖先
  • 如果左子树找到,那就返回左子树的结果;

如果右子树找到,那就返回右子树的结果;

在这里插入图片描述

方法二:栈存路径法

获取路径

通过深度优先搜索,分别找到从根节点到p和q的路径中所有节点,并分别存入两个栈中

对齐栈长度

比较两个栈中元素长度,将较长的栈中元素弹出,然后比较两个栈中的下一深度元素是否相等

找公共祖先

同时弹出两个栈中不相等的栈顶元素,第一个相同的节点就是最近公共祖先

在这里插入图片描述

三、代码展示

方法一:递归法

publicTreeNodelowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q){//当前节点为空,直接返回nullif(root ==null){returnnull;}//当前节点是p或者q,返回当前节点(祖先)if(root == p || root == q){return root;}//递归探索左子树,找到p和q的最近祖先TreeNode leftRet =lowestCommonAncestor(root.left, p, q);//递归探索右子树,找到p和q的最近祖先TreeNode rightRet =lowestCommonAncestor(root.right, p, q);//左子树和右子树都找到if(leftRet !=null&& rightRet !=null){return root;//左子树找到}elseif(leftRet !=null){return leftRet;}else{//右子树找到return rightRet;}}

方法二:栈存路径法

publicbooleangetPath(TreeNode root,TreeNode node,Stack<TreeNode> stack){if(root ==null){returnfalse;}//将当前节点压入路径 stack.push(root);//找到目标节点if(root == node){returntrue;}//递归搜索左子树boolean flg =getPath(root.left,node,stack);if(flg){returntrue;}//递归搜索右子树 flg =getPath(root.right,node,stack);if(flg){returntrue;}//左右子树都没找到,弹出当前节点 stack.pop();returnfalse;}publicTreeNodelowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q){if(root ==null){returnnull;}//找到跟到p和q的路径Stack<TreeNode> stackp =newStack<>();Stack<TreeNode> stackq =newStack<>();getPath(root,p,stackp);getPath(root,q,stackq);//对齐两个栈长度int sizep = stackp.size();int sizeq = stackq.size();int size = sizep-sizeq;if(size>0){//stacp更长,弹出多余元素while(size !=0){ stackp.pop(); size--;}}else{////stacq更长,弹出多余元素 size = sizeq-sizep;while(size !=0){ stackq.pop(); size--;}}//此时两个栈大小一样,//同时弹出栈顶,找到相同的while(!stackp.isEmpty()&&!stackq.isEmpty()){if(stackp.peek().equals(stackq.peek())){return stackp.peek();} stackp.pop(); stackq.pop();}returnnull;}

四、总结

通过递归法的“自底向上”回溯,以及栈存路径法的“路径对比”思路,我们可以高效求解二叉树的最近公共祖先问题——前者利用递归特性简化代码,后者通过显式路径存储更直观易懂。结合本文的思路分析与代码示例,你可以根据场景灵活选择解法,轻松应对这类二叉树算法题。

Read more

Flutter 组件 humanize 的适配 鸿蒙Harmony 深度进阶 - 驾驭多语言复数逻辑算法、实现鸿蒙端中式大额单位感知与极致人性化文本渲染方案

Flutter 组件 humanize 的适配 鸿蒙Harmony 深度进阶 - 驾驭多语言复数逻辑算法、实现鸿蒙端中式大额单位感知与极致人性化文本渲染方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 humanize 的适配 鸿蒙Harmony 深度进阶 - 驾驭多语言复数逻辑算法、实现鸿蒙端中式大额单位感知与极致人性化文本渲染方案 前言 在前文我们掌握了 humanize 进行基础数据转换的方法。但在鸿蒙(OpenHarmony)面向全球市场的布局中,真正的技术挑战往往隐藏在极其琐碎的“语言表达”中。例如:在英文中我们说 1 items 是错误的,必须是 1 item 与 2 items;而在中文环境下,我们虽然没有复数形变,但却有“万、亿”这类独特的四位一级计数逻辑。 一个真正具备“高级感”的鸿蒙应用,不应在数据展示上显得僵硬且带有明显的机器翻译痕迹。 本文将作为 humanize 适配的进阶篇,带你攻克多语言复数(Pluralization)

By Ne0inhk
【算法】双指针(一)-移动零

【算法】双指针(一)-移动零

目录 一、题目介绍 二、双指针原理 1.当前维护指针 1.1维护方向 (1)条件边界 三、三指针原理 应用 1.快速排序 2.快速选择找元素 2.1找第k大元素 2.2找等于k元素 四、提交代码 一、题目介绍 283. 移动零 - 力扣(LeetCode) 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums = [0,1,0,3,12] 输出: [1,

By Ne0inhk
【优选算法必刷100题:专题六】(模拟算法)第039~343题:替换所有的问号、提莫攻击、Z 字形变换、外观数列、数青蛙

【优选算法必刷100题:专题六】(模拟算法)第039~343题:替换所有的问号、提莫攻击、Z 字形变换、外观数列、数青蛙

🎬 个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》 ⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平 🎬 艾莉丝的简介: 🎬艾莉丝的算法专栏简介: 文章目录 * 039 替换所有的问号 * 1.1 解法:模拟的思想 * 1.2 算法实现 * 1.3 博主手记 * 040 提莫攻击 * 2.1 解法:模拟 + 分情况讨论 * 2.2 算法实现 * 2.3 博主手记 * 041 Z 字形变换

By Ne0inhk

B树与B+树:从原理到实现的深度解析

第1章:引言:为什么需要多路平衡查找树? 1.1 计算机存储层次结构带来的挑战 在现代计算机系统中,存储层次结构从寄存器、高速缓存、主内存到磁盘,访问速度差异巨大。以典型的现代计算机为例: * CPU寄存器:访问延迟约0.3纳秒 * L1高速缓存:访问延迟约0.9纳秒 * L2高速缓存:访问延迟约2.8纳秒 * 主内存:访问延迟约12纳秒 * 固态硬盘:访问延迟约25微秒(25,000纳秒) * 机械硬盘:访问延迟约10毫秒(10,000,000纳秒) 这种访问延迟的指数级差异催生了局部性原理的优化思想。然而,当数据规模达到千万甚至亿级时,传统的二叉树结构面临严重挑战: text 二叉查找树的最坏情况: [1] \ [2] \ [3] \ ... \ [1000000] 1.2 二叉树在外部存储中的局限性 考虑一个包含1亿条记录的数据库,假设每个记录需要100字节存储空间。如果使用二叉查找树: * 平均树高约为log₂(100,

By Ne0inhk