【算法训练-二叉树 五】【最近公共祖先】二叉树的最近公共祖先、二叉搜索树的最近公共祖先

简介: 【算法训练-二叉树 五】【最近公共祖先】二叉树的最近公共祖先、二叉搜索树的最近公共祖先

废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【二叉树的节点查找】,使用【二叉树】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为:目标公司+最近一年+出现频率排序,由高到低的去牛客TOP101去找,只有两个地方都出现过才做这道题(CodeTop本身汇聚了LeetCode的来源),确保刷的题都是高频要面试考的题。

明确目标题后,附上题目链接,后期可以依据解题思路反复快速练习,题目按照题干的基本数据结构分类,且每个分类的第一篇必定是对基础数据结构的介绍

二叉搜索树的最近公共祖先【MID】

同上一题的解法,再练习一遍

题干

解题思路

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

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

  1. 终止条件:当越过叶节点,则直接返回 null;当 root 等于 p,q,则直接返回 root;
  2. 递推工作: 开启递归左子节点,返回值记为 left;开启递归右子节点,返回值记为 right;
  3. 返回值: 根据 left和 right,可展开为四种情况;
  • 当 left 和 righ同时为空 :说明一直遍历到叶节点, root 的左 / 右子树中都不包含 p,q ,返回 null;
  • 当 left和 right 同时不为空 :说明 pq 分列在 roo的 异侧 (分别在 左 / 右子树),因此 root为最近公共祖先,返回 root
  • 当 left为空 ,right不为空 :pq 都不在 root 的左子树中,直接返回 right。具体可分为两种情况:pq 其中一个在 root 的 右子树 中,此时 right指向 p(假设为 p );pq 两节点都在 root的 右子树 中,此时的 right指向 最近公共祖先节点 ;
  • 当 left不为空 , right为空 :与情况 3. 同理

代码实现

给出代码实现基本档案

基本数据结构二叉树

辅助数据结构

算法递归、DFS

技巧

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

public TreeNode lowestCommonAncestor (TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        return dfsFindRoot(root, p, q);
    }
    private TreeNode dfsFindRoot(TreeNode node, TreeNode p, TreeNode q) {
        // 1 递归终止条件:越过叶子节点没找到;root为p或root为q
        if (node == null || node == p || node == q) {
            return node;
        }
        // 2 本级任务:分别在左右子树中寻找节点
        TreeNode left = dfsFindRoot(node.left, p, q);
        TreeNode right = dfsFindRoot(node.right, p, q);
        // 3 本级任务:分别在左右子树中寻找节点
        if (left != null && right != null) {
            //如果两个节点分别存在于左右子树,则当前节点即是最近公共祖先
            return node;
        }
        // 如果两个节点在root的同侧,则返回的节点即为公共节点
        if (right == null && left != null) {
            return left;
        }
        if (right != null && left == null) {
            return right;
        }
        // 如果两个节点左右都没有找到,则返回null
        return null;
    }

复杂度分析

时间复杂度:O(n),其中n为二叉树的节点数,遍历整棵二叉树

空间复杂度:O(n),最坏情况下,二叉树化为链表,递归栈深度最大为n

二叉树的最近公共祖先【MID】

最近公共祖先(Lowest Common Ancestor, LCA)是指在一个二叉树中,两个节点p和q的最低公共祖先节点,即深度最深且同时是p和q的祖先的节点,同时节点本身也可以为其最近公共祖先

题干

解题思路

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

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

  1. 终止条件:当越过叶节点,则直接返回 null;当 root 等于 p,q,则直接返回 root;
  2. 递推工作: 开启递归左子节点,返回值记为 left;开启递归右子节点,返回值记为 right;
  3. 返回值: 根据 left和 right,可展开为四种情况;
  • 当 left 和 righ同时为空 :说明一直遍历到叶节点, root 的左 / 右子树中都不包含 p,q ,返回 null;
  • 当 left和 right 同时不为空 :说明 pq 分列在 roo的 异侧 (分别在 左 / 右子树),因此 root为最近公共祖先,返回 root
  • 当 left为空 ,right不为空 :pq 都不在 root 的左子树中,直接返回 right。具体可分为两种情况:pq 其中一个在 root 的 右子树 中,此时 right指向 p(假设为 p );pq 两节点都在 root的 右子树 中,此时的 right指向 最近公共祖先节点 ;
  • 当 left不为空 , right为空 :与情况 3. 同理

代码实现

给出代码实现基本档案

基本数据结构二叉树

辅助数据结构

算法递归、DFS

技巧

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;
/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param root TreeNode类
     * @param p int整型
     * @param q int整型
     * @return int整型
     */
    public int lowestCommonAncestor (TreeNode root, int p, int q) {
        if (root == null) {
            return -1;
        }
        return dfsCheck(root, p, q);
    }
    private int dfsCheck(TreeNode node, int p, int q) {
        // 1 递归终止条件,找到了则返回值,超过叶子节点没找到则返回-1
        if (node == null) {
            return -1;
        }
        if ( node.val == p || node.val == q) {
            return node.val;
        }
        // 2 获取左右子树的查找结果
        int rightVal = dfsCheck(node.right, p, q);
        int leftVal = dfsCheck(node.left, p, q);
        // 3 依据查找结果判断取值
        if (rightVal != -1 && leftVal != -1) {
            // p、q分别在左右子树,且都不作为公共祖先
            return node.val;
        } else if (rightVal == -1) {
            // 右子树没有找到,左子树找到了,则返回左子树找到的最近公共祖先
            return leftVal;
        } else if (leftVal == -1) {
            // 左子树没有找到,右子树找到了,则返回右子树找到的最近公共祖先
            return rightVal;
        } else {
            // 两边都没找到,返回-1;
            return -1;
        }
    }
}

复杂度分析

时间复杂度:O(n),其中n为二叉树的节点数,遍历整棵二叉树

空间复杂度:O(n),最坏情况下,二叉树化为链表,递归栈深度最大为n

拓展知识:二叉树的最近公共祖先

最近公共祖先(Lowest Common Ancestor, LCA)是指在一个二叉树中,两个节点p和q的最低公共祖先节点,即深度最深且同时是p和q的祖先的节点。以下是一种基于递归的实现思路,并提供Java的实现代码:

定义

假设有一个二叉树,每个节点包含如下信息:

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int val) {
        this.val = val;
        this.left = null;
        this.right = null;
    }
}

实现思路

  1. 从根节点开始遍历二叉树。
  2. 对于当前节点,递归地查找其左子树和右子树,看看是否能找到节点p和节点q。
  3. 如果左子树中找到了节点p或节点q,或者右子树中找到了节点p或节点q,那么当前节点就是它们的最近公共祖先。
  4. 如果左子树和右子树都找到了最近公共祖先,那么当前节点就是它们的最近公共祖先。
  5. 最后,返回最近公共祖先。

Java实现代码

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    // Base case: 如果当前节点为空或者等于p或q中的任意一个,直接返回当前节点
    if (root == null || root == p || root == q) {
        return root;
    }
    // 递归查找左子树和右子树
    TreeNode leftLCA = lowestCommonAncestor(root.left, p, q);
    TreeNode rightLCA = lowestCommonAncestor(root.right, p, q);
    // 如果左子树和右子树都找到了最近公共祖先,那么当前节点就是最近公共祖先
    if (leftLCA != null && rightLCA != null) {
        return root;
    }
    // 如果只在一侧找到了最近公共祖先,返回那一侧找到的结果
    return (leftLCA != null) ? leftLCA : rightLCA;
}

这段代码会在二叉树中查找节点p和q的最近公共祖先,并返回该节点。如果一个节点是另一个节点的祖先,那么它自己也可以是自己的祖先,因此在递归中会检查当前节点是否等于p或q中的任意一个,如果是就返回当前节点。否则,递归地查找左子树和右子树,并根据左右子树的结果确定最近公共祖先。

相关文章
|
1月前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
1月前
|
存储 缓存 算法
如何提高二叉树遍历算法的效率?
选择合适的遍历算法,如按层次遍历树时使用广度优先搜索(BFS),中序遍历二叉搜索树以获得有序序列。优化数据结构,如使用线索二叉树减少空指针判断,自定义节点类增加辅助信息。利用递归与非递归的特点,避免栈溢出问题。多线程并行遍历提高速度,注意线程安全。缓存中间结果,避免重复计算。预先计算并存储信息,提高遍历效率。综合运用这些方法,提高二叉树遍历算法的效率。
58 5
|
2月前
|
存储 机器学习/深度学习 算法
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
蓝桥杯Python编程练习题的集合,涵盖了从基础到提高的多个算法题目及其解答。
119 3
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
|
1月前
|
机器学习/深度学习 JSON 算法
二叉树遍历算法的应用场景有哪些?
【10月更文挑战第29天】二叉树遍历算法作为一种基础而重要的算法,在许多领域都有着不可或缺的应用,它为解决各种复杂的问题提供了有效的手段和思路。随着计算机科学的不断发展,二叉树遍历算法也在不断地被优化和扩展,以适应新的应用场景和需求。
43 0
|
1月前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
2月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
32 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
18天前
|
算法
基于WOA算法的SVDD参数寻优matlab仿真
该程序利用鲸鱼优化算法(WOA)对支持向量数据描述(SVDD)模型的参数进行优化,以提高数据分类的准确性。通过MATLAB2022A实现,展示了不同信噪比(SNR)下模型的分类误差。WOA通过模拟鲸鱼捕食行为,动态调整SVDD参数,如惩罚因子C和核函数参数γ,以寻找最优参数组合,增强模型的鲁棒性和泛化能力。
|
24天前
|
机器学习/深度学习 算法 Serverless
基于WOA-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目利用鲸鱼优化算法(WOA)优化支持向量机(SVM)参数,针对乳腺癌早期诊断问题,通过MATLAB 2022a实现。核心代码包括参数初始化、目标函数计算、位置更新等步骤,并附有详细中文注释及操作视频。实验结果显示,WOA-SVM在提高分类精度和泛化能力方面表现出色,为乳腺癌的早期诊断提供了有效的技术支持。
|
4天前
|
供应链 算法 调度
排队算法的matlab仿真,带GUI界面
该程序使用MATLAB 2022A版本实现排队算法的仿真,并带有GUI界面。程序支持单队列单服务台、单队列多服务台和多队列多服务台三种排队方式。核心函数`func_mms2`通过模拟到达时间和服务时间,计算阻塞率和利用率。排队论研究系统中顾客和服务台的交互行为,广泛应用于通信网络、生产调度和服务行业等领域,旨在优化系统性能,减少等待时间,提高资源利用率。
|
11天前
|
存储 算法
基于HMM隐马尔可夫模型的金融数据预测算法matlab仿真
本项目基于HMM模型实现金融数据预测,包括模型训练与预测两部分。在MATLAB2022A上运行,通过计算状态转移和观测概率预测未来值,并绘制了预测值、真实值及预测误差的对比图。HMM模型适用于金融市场的时间序列分析,能够有效捕捉隐藏状态及其转换规律,为金融预测提供有力工具。