二叉搜索树、二叉树的最近公共祖先

简介: 二叉搜索树、二叉树的最近公共祖先

前言

祖先的定义: 若节点 p 在节点 root 的左(右)子树中,或 p = root,则称 root 是 p 的祖先。

最近公共祖先的定义: 设节点 root 为节点 p,q 的某公共祖先,若其左子节点 root.left 和右子节点 root.right 都不是 p,q 的公共祖先,则称 root 是 “最近的公共祖先” 。

根据以上定义,若 root 是 p,q 的 最近公共祖先 ,则只可能为以下情况之一:

  1. p 和 q 在 root 的子树中,且分列 root 的 异侧(即分别在左、右子树中);
  2. p=root ,且 q 在 root 的左或右子树中;
  3. q=root ,且 p 在 root 的左或右子树中;

网络异常,图片无法展示
|

Leetcode-235. 二叉搜索树的最近公共祖先

分析

先来回顾下二叉搜索树的相关性质:

二叉搜索树按照中序遍历是有序的,左孩子所有结点的值都小于根结点,右孩子所有结点的值大于根结点

本题使用递归的方法:

  • 如果p,q的值均小于根结点,则在左孩子中找公共祖先
  • 如果p,q的值均大于根结点,则在右孩子中找公共祖先
  • 否则,返回根结点,包含三种情况(1)p=root (2) q=root (3) p,q在root的两侧。

代码实现(Javascript版)

var lowestCommonAncestor = function(root, p, q) {
    if(p.val < root.val && q.val < root.val){
        return lowestCommonAncestor(root.left, p, q);
    }
    if(p.val > root.val && q.val > root.val){
        return lowestCommonAncestor(root.right, p, q);
    }
    return root
};
复制代码

Leetcode-236. 二叉树的最近公共祖先

分析

对于二叉树的两个节点node1和node2,找到他们的最低公共祖先节点,会有两种情况:

  1. node1是node2的最低公共祖先,或node2是node1的最低公共祖先
  2. node1与node2不互为最低公共祖先(不在一个子树上,需要通过向上汇聚才能找到最低公共祖先)

思路

二叉树没有二叉搜索树的性质,这里依然使用递归解决,通过递归对二叉树进行先序遍历,当遇到节点 node1 或 node2时返回。从底至顶回溯,当节点 node1,node2 在节点 root 的异侧时,节点 root 即为最近公共祖先,则向上返回 root。

lowestAncestor函数实现了:

  1. 若node1、node2其中之一等于根结点root,则返回node1,node2 (找到node1或node2,此时返回的不一定是最近公共祖先,有可能另一个结点找不到)
  2. 否则,返回最近公共祖先

递归解析

终止条件:

  1. 当遍历到叶节点,则直接返回null
  2. 当root等于node1、node2,则直接返回root递推工作:
  3. 开启递归左子节点,返回值记为left
  4. 开启递归右子节点,返回值记为right

分析返回值:

根据left和right,可展开为四种情况:

  1. 当left和right同时为空,说明root的左/右子树都不包含node1、node2,返回null
  2. 当left和right同时不为空,说明node1、node2分列在root的异侧(分别在左/右子树),因此root为最近公共祖先,返回root
  3. left为空,right不为空,node1、node2都不在root的左子树中,直接返回right。此处一般有两种情况:
  • 1)node1、node2其中一个在root的右子树中,此时right指向node1(假设为node1)
  • 2)node1、node2两节点都在root的右子树中,此时的right指向最近公共祖先节点
  1. left不为空,right为空,与情况3雷同

代码实现

Java版

// 易理解版
public static Node lowestAncestor(Node head, Node o1, Node o2){
    if(head == null || head == o1 || head == o2){
        return head;
    }
    Node left = lowestAncestor(head.left, o1, o2);
    Node right = lowestAncestor(head.right, o1, o2);
   if(left == null && right == null) return null; // 1.
   if(left == null) return right; // 3.
   if(right == null) return left; // 4.
   return root; // 情况2. 相当于if(left != null and right != null)
}
// 进一步简写
 public static Node lowestAncestor(Node head, Node o1, Node o2){
    if(head == null || head == o1 || head == o2){
        return head;
    }
    Node left = lowestAncestor(head.left, o1, o2);
    Node right = lowestAncestor(head.right, o1, o2);
    // 不可能左边和右边都找到了公共祖先,只可能是左边和右边分别找到了o1、o2,返回根节点
    if(left != null && right != null){
        return head;
    }
    // 左边找不到o1或o2或公共祖先,o1、o2都在右孩子;
    // 右边找不到o1或o2或公共祖先,o1、o2都在左孩子;
    return left != null ? left : right;
}
复制代码

Javascript版

var lowestCommonAncestor = function(root, p, q) {
    if(root == p || root == q || !root) return root;
    let left = lowestCommonAncestor(root.left, p, q);
    let right = lowestCommonAncestor(root.right, p, q);
    if(!left && !right) return null;
    if(left && right){
        return root
    }
    return left || right;
};



相关文章
|
前端开发 C# 数据库
.NET中使用BootstrapBlazor组件库Table实操篇
.NET中使用BootstrapBlazor组件库Table实操篇
363 0
|
存储 小程序 算法
【微信小程序】粤语教学平台-粤言粤语(上)
【微信小程序】粤语教学平台-粤言粤语
632 0
|
算法 数据可视化 机器人
ubuntu16.04下ROS操作系统学习笔记(九)Moveit(上)
ubuntu16.04下ROS操作系统学习笔记(九)Moveit(上)
789 0
|
10月前
|
人工智能 缓存 Cloud Native
解锁 DeepSeek 安全接入、稳定运行新路径
聚焦于企业部署 DeepSeek 的应用需求,本文介绍了模型权重下载及多种部署方案,还阐述了大模型应用落地的常见需求,帮助用户逐步提升模型应用效果。
1384 238
|
9月前
|
运维 监控 算法
时间序列异常检测:MSET-SPRT组合方法的原理和Python代码实现
MSET-SPRT是一种结合多元状态估计技术(MSET)与序贯概率比检验(SPRT)的混合框架,专为高维度、强关联数据流的异常检测设计。MSET通过历史数据建模估计系统预期状态,SPRT基于统计推断判定偏差显著性,二者协同实现精准高效的异常识别。本文以Python为例,展示其在模拟数据中的应用,证明其在工业监控、设备健康管理及网络安全等领域的可靠性与有效性。
1062 13
时间序列异常检测:MSET-SPRT组合方法的原理和Python代码实现
|
JavaScript C++
基于QtQuick的QCustomPlot实现
本文介绍了如何在QtQuick中实现基于QCustomPlot的图表绘制,包括效果图展示、C++和QML方面的实现代码、注意事项以及应用场景。作者提供了源码下载链接,方便读者学习和使用QCustomPlot进行QtQuick应用程序中的图表绘制。
528 4
基于QtQuick的QCustomPlot实现
uniapp使用路由名称跳转
【9月更文挑战第11天】在UniApp中,可通过定义路由名称实现页面跳转,需在`pages.json`中设置页面的`name`属性。使用`uni.navigateTo`等API并指定名称即可跳转,例如`name: &#39;detailPage&#39;`。目标页面可在`onLoad`函数中获取传递的参数,这种方式使代码更清晰且便于维护,尤其适合大型项目。
449 1
|
11月前
|
机器学习/深度学习 人工智能 自然语言处理
《预训练语言模型:开启智能时代的大门》
预训练语言模型如BERT和GPT是当今AI领域的核心技术,广泛应用于自然语言处理。训练过程包括数据准备、模型架构(如Transformer)、掩码语言模型和下一句预测等方法。应用场景涵盖文本分类、情感分析、问答系统和语言生成等。BERT擅长理解任务,GPT则在生成任务中表现优异。未来,预训练模型将继续优化并拓展应用领域。
242 9
|
8月前
|
存储 Java 数据挖掘
Java 中数组的多种定义方式
本文深入解析了Java中数组的多种定义方式,涵盖基础的`new`关键字创建、直接初始化、动态初始化,到多维数组、`Arrays.fill()`方法以及集合类转换为数组等高级用法。通过理论与实践结合的方式,探讨了每种定义方法的适用场景、优缺点及其背后的原理,帮助开发者掌握高效、灵活的数组操作技巧,从而编写更优质的Java代码。
434 0
|
传感器 机器学习/深度学习 人工智能
《智领新材制造:人工智能点亮新材料良品率提升之路》
在新材料生产中,人工智能通过精准监测、故障预警、智能优化工艺参数、智能化质量检测及预测性维护,全方位提升生产良品率。它结合传感器实时数据,快速识别异常并优化参数,确保产品质量一致性。机器视觉和无损检测技术提高缺陷识别精度,预测性维护保障设备稳定运行。尽管面临挑战,AI正重塑新材料生产模式,助力产业高质量发展。
241 4

热门文章

最新文章