二叉树题目:二叉树的最近公共祖先

news2024/9/23 11:27:13

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:二叉树的最近公共祖先

出处:236. 二叉树的最近公共祖先

难度

5 级

题目描述

要求

给定一个二叉树,找到该树中两个指定结点的最近公共祖先。

最近公共祖先的定义为:两个结点 p \texttt{p} p q \texttt{q} q 的最近公共祖先是满足 p \texttt{p} p q \texttt{q} q 都是其后代的最低的结点 T \texttt{T} T(一个结点也可以是它自己的祖先)。

示例

示例 1:

示例 1

输入: root   =   [3,5,1,6,2,0,8,null,null,7,4],   p   =   5,   q   =   1 \texttt{root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1} root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3 \texttt{3} 3
解释:结点 5 \texttt{5} 5 和结点 1 \texttt{1} 1 的最近公共祖先是结点 3 \texttt{3} 3

示例 2:

示例 2

输入: root   =   [3,5,1,6,2,0,8,null,null,7,4],   p   =   5,   q   =   4 \texttt{root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4} root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5 \texttt{5} 5
解释:结点 5 \texttt{5} 5 和结点 4 \texttt{4} 4 的最近公共祖先是结点 5 \texttt{5} 5。因为根据定义最近公共祖先结点可以为结点本身。

示例 3:

输入: root   =   [1,2],   p   =   1,   q   =   2 \texttt{root = [1,2], p = 1, q = 2} root = [1,2], p = 1, q = 2
输出: 1 \texttt{1} 1

数据范围

  • 树中结点数目在范围 [2,   10 5 ] \texttt{[2, 10}^\texttt{5}\texttt{]} [2, 105]
  • -10 9 ≤ Node.val ≤ 10 9 \texttt{-10}^\texttt{9} \le \texttt{Node.val} \le \texttt{10}^\texttt{9} -109Node.val109
  • 所有 Node.val \texttt{Node.val} Node.val 各不相同
  • p ≠ q \texttt{p} \ne \texttt{q} p=q
  • p \texttt{p} p q \texttt{q} q 均存在于给定的二叉树中

解法一

思路和算法

二叉树中的每个结点对应一条从根结点到该结点的路径。对于结点 p p p q q q,其对应的路径一定存在重合部分,重合部分的结点都是 p p p q q q 的公共祖先,其中深度最大的公共祖先即为最近公共祖先。

考虑示例 1 和示例 2 使用的二叉树。

图 1

示例 1 中, p = 5 p = 5 p=5 q = 1 q = 1 q=1。结点 5 5 5 对应的路径是 [ 3 , 5 ] [3, 5] [3,5],结点 1 1 1 对应的路径是 [ 3 , 1 ] [3, 1] [3,1],唯一的公共祖先是 3 3 3,最近公共祖先是 3 3 3

示例 2 中, p = 5 p = 5 p=5 q = 4 q = 4 q=4。结点 5 5 5 对应的路径是 [ 3 , 5 ] [3, 5] [3,5],结点 4 4 4 对应的路径是 [ 3 , 5 , 2 , 4 ] [3, 5, 2, 4] [3,5,2,4],公共祖先是 3 3 3 5 5 5,最近公共祖先是 5 5 5

当遍历到一个结点时,即可得到从根结点到该结点的路径,将路径反转之后即可得到从该结点到根结点的路径。对于结点 p p p q q q 分别得到从当前结点到根结点的路径,两条路径相交处的结点即为最近公共祖先。

为了得到反向路径,需要使用哈希表存储每个结点的父结点。对二叉树深度优先搜索,在遍历过程中将每个结点的父结点存入哈希表。遍历结束之后,从结点 p p p 出发,每次移动到当前结点的父结点,直到到达根结点时,即得到从结点 p p p 到根结点的路径。在寻找从结点 p p p 到根结点的路径的过程中,使用哈希集合存储访问过的结点。然后从结点 q q q 出发,每次移动到当前结点的父结点,寻找从结点 q q q 到根结点的路径,第一个遇到的在哈希集合中的结点即为最近公共祖先。

代码

class Solution {
    Map<TreeNode, TreeNode> parentMap = new HashMap<TreeNode, TreeNode>();
    Set<TreeNode> visited = new HashSet<TreeNode>();

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        dfs(root);
        TreeNode node = p;
        while (node != null) {
            visited.add(node);
            node = parentMap.get(node);
        }
        TreeNode ancestor = q;
        while (!visited.contains(ancestor)) {
            ancestor = parentMap.get(ancestor);
        }
        return ancestor;
    }

    public void dfs(TreeNode node) {
        TreeNode left = node.left, right = node.right;
        if (left != null) {
            parentMap.put(left, node);
            dfs(left);
        }
        if (right != null) {
            parentMap.put(right, node);
            dfs(right);
        }
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。深度优先搜索访问每个结点一次,需要 O ( n ) O(n) O(n) 的时间,对于结点 p p p 和结点 q q q,寻找从当前结点到根结点的路径的时间不超过 O ( n ) O(n) O(n),因此总时间复杂度是 O ( n ) O(n) O(n)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是哈希表、哈希集合和栈的空间,哈希表需要 O ( n ) O(n) O(n) 的空间存储每个结点的父结点,哈希集合和栈的空间取决于二叉树的高度,二叉树的高度不超过 O ( n ) O(n) O(n),因此总空间复杂度是 O ( n ) O(n) O(n)

解法二

思路和算法

考虑二叉树中的每个结点,可能有以下情况。

  1. 如果 p p p q q q 中的一个结点是当前结点,另一个结点在以当前结点为根结点的子树中,则当前结点即为 p p p q q q 的最近公共祖先。

  2. 如果 p p p q q q 分别位于当前结点的左子树和右子树中,则当前结点即为 p p p q q q 的最近公共祖先。

  3. 如果 p p p q q q 位于当前结点的同一个子树中,则 p p p q q q 的最近公共祖先位于当前结点的同一个子树中。

上述情况中的情况 1 和情况 2 为当前结点是 p p p q q q 的最近公共祖先,对于情况 3,需要在子树中继续寻找 p p p q q q 的最近公共祖先,最近公共祖先一定满足情况 1 或情况 2。因此,最近公共祖先可能的情况一共有两种。

为了寻找最近公共祖先,需要从根结点开始深度优先搜索。对于每个结点,在当前子树中寻找 p p p q q q 是否存在,如果 p p p q q q 都存在则返回最近公共祖先,如果 p p p q q q 只存在一个则返回存在的结点。以下将 p p p q q q 统称为「目标结点」。

具体做法如下。

  1. 如果当前结点为 p p p q q q,则返回当前结点。

  2. 否则,在当前结点的左子树和右子树中分别寻找 p p p q q q

    • 如果两个子树中都存在目标结点,则 p p p q q q 分别在两个子树中,当前结点即为最近公共祖先。

    • 如果只有一个子树中存在目标结点,则在该子树中寻找最近公共祖先。

上述过程是一个递归的过程,递归的终止条件是当前结点为 p p p q q q,其余情况则调用递归。

对于每个结点,首先访问其子结点寻找目标结点和公共祖先,然后根据访问子结点的结果得到当前结点的结果。计算结果的顺序是先计算子结点的结果,后计算当前结点的结果,该顺序实质是后序遍历。由于在计算每个结点的结果时,该结点的子结点的结果已知,该结点的结果由子结点的结果决定,因此可以确保结果正确。

代码

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == p || root == q) {
            return root;
        }
        TreeNode leftAncestor = null, rightAncestor = null;
        if (root.left != null) {
            leftAncestor = lowestCommonAncestor(root.left, p, q);
        }
        if (root.right != null) {
            rightAncestor = lowestCommonAncestor(root.right, p, q);
        }
        if (leftAncestor != null && rightAncestor != null) {
            return root;
        }
        return leftAncestor != null ? leftAncestor : rightAncestor;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是递归调用的栈空间,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1230251.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode刷题】--9.回文数

9.回文数 class Solution {public boolean isPalindrome(int x) {if(x < 0){return false;}int tmp x, sum 0;boolean flag false;while(x ! 0){sum sum * 10 x % 10;x / 10;}if(sum tmp){flag true;}return flag;} }

动画师如何选择全身动捕设备制作动画?

随着行业的不断发展&#xff0c;全身动捕设备在动画制作行业的应用已十分普遍。全身动捕设备分为光学动捕设备和惯性动捕设备&#xff0c;动画师该如何选择全身动捕设备制作动画&#xff1f; 与光学动捕设备相比&#xff0c;惯性动捕设备对场地和空间的限制更小&#xff0c;不…

vulnhub靶机Presidential

靶机地址&#xff1a;https://download.vulnhub.com/presidential/Presidential.ova 主机发现 arp-scan -l 端口扫描 nmap --min-rate 10000 192.168.21.150 端口服务扫描 nmap -sV -sT -O -p80 192.168.21.150 漏洞扫描 nmap --scriptvuln -p80 192.168.21.150 只有一个端…

NodeMCU ESP8266构建Web Server网页端控制设备

NodeMCU ESP8266构建Web Server网页端控制设备 前言 NodeMCU ESP8266 内部集成了TCP/IP协议栈&#xff0c;可以快速构建网络功能&#xff0c;搭建联网应用的硬件平台&#xff1b; ESP8266可以作为WiFi接入点&#xff08;Station&#xff09;&#xff0c;这样可以方便连接互联…

如何修改dpi为300?96dpi怎么改成300dpi?

平时使用的图片dpi一般都是96&#xff0c;但是我们在打印的时候&#xff0c;都要求dpi为300以上&#xff0c;这时候就需要修改图片分辨率&#xff0c;如何改图片分辨率成了一个问题&#xff0c;所以今天就教大家一个图片分辨率提高在线处理的方法&#xff0c;一起来了解一下吧。…

右键菜单和弹出菜单的区别

接触windows开发10年了&#xff0c;一直以为"右键菜单"和"弹出菜单"是不同的。 最近刚刚发现&#xff0c;这两种菜单在定义的时候和消息循环处理程序中并没有什么不同&#xff0c;区别只是在于windows底层显示方式。 如下是右键菜单的显示方式&#xff1…

【图像分类】【深度学习】【Pytorch版本】GoogLeNet(InceptionV4)模型算法详解

【图像分类】【深度学习】【Pytorch版本】GoogLeNet(InceptionV4)模型算法详解 文章目录 【图像分类】【深度学习】【Pytorch版本】GoogLeNet(InceptionV4)模型算法详解前言GoogLeNet(InceptionV4)讲解Stem结构Inception-A结构Inception- B结构Inception-C结构redution-A结构re…

4-1三个整数的最大数

#include<stdio.h> int main(){int a,b,c,t,max;printf("请输入三个数&#xff1a;");scanf("%d,%d,%d",&a,&b,&c);t(a>b)?a:b;max(t>c)?t:c;printf("输出三个数中最大是数字是&#xff1a;%d",max);return 0;}

单链表相关面试题--3.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点

/* 解题思路&#xff1a; 通过快慢指针找到中间节点&#xff0c;快指针每次走两步&#xff0c;慢指针每次走一步&#xff0c;当快指针走到结尾的时候&#xff0c;慢指针正好走到中间位置 */ typedef struct ListNode Node; struct ListNode* middleNode(struct ListNode* head)…

全面解读数据资产入表风口,与企业有什么关系和影响

亿信华辰『数据资产入表实务系列直播』第一期成功举办。我们邀请到了亿信华辰高级顾问专家、湖北省软件企业协会数据要素发展工作委员会专家委员刘晗为大家带来数据入表知识体系分享&#xff0c;全程干货&#xff0c;深度解读&#xff0c;以下是演讲上半部分直播内容。 一、数据…

Netty源码学习4——服务端是处理新连接的netty的reactor模式

零丶引入 在前面的源码学习中&#xff0c;梳理了服务端的启动&#xff0c;以及NioEventLoop事件循环的工作流程&#xff0c;并了解了Netty处理网络io重要的Channel &#xff0c;ChannelHandler&#xff0c;ChannelPipeline。 这一篇将学习服务端是如何构建新的连接。 一丶网络包…

​使用SPDK lib搭建自己的NVMe-oF Target应用​

一. 背景 NVMe是一种抽象的传输协议层&#xff0c;旨在提供可靠的NVMe命令和数据传输。NVMe over Fabric (NVMe-oF) 实现了NVMe标准在PCIe总线上的扩展&#xff0c;可以支持数据中心的网络存储&#xff0c;它支持多种传输方式包括FC&#xff0c;RDMA和TCP。NVMe-oF服务(应用) 是…

【已解决】移动号码在移动网上营业厅更换为8元保号套餐

有很多人的副卡基本是为了接收银行卡短信&#xff0c;平时基本不打电话和用流量&#xff0c;每个月固定消费在18-30左右&#xff0c;很浪费。今天发现在网上营业厅就可以修改8元保号套餐&#xff0c;分享给大家。 保号套餐 有以下两种&#xff1a; 解决办法&#xff1a; 1、…

redis非关系型数据库

redis非关系型数据库&#xff0c;缓存型数据库 关系型数据库和非关系型数据库的区别 关系型数据库 关系型数据库是一个结构化的数据库。 记录方式&#xff1a;行和列 行的作用&#xff1a;记录对象的属性 列的作用&#xff1a;声明对象 表与表之间是有关联的&#xff1a…

LR学习笔记——初识lightroom

文章目录 介绍图库界面修改照片界面 介绍 Lightroom是Adobe公司开发的一款用于图片后期处理制作的软件&#xff0c;被称为Adobe Photoshop Lightroom。其增强的校正工具、强大的组织功能以及灵活的打印选项可以帮助加快图片后期处理速度&#xff0c;将更多的时间投入拍摄。 相…

Web自动化测试工具的关键功能

当涉及到 Web 应用程序开发和维护&#xff0c;自动化测试工具发挥着至关重要的作用。这些工具不仅仅简化了测试过程&#xff0c;还有助于保障软件质量、提高生产力和减少人力资源的投入。以下是Web自动化测试工具提供的关键功能&#xff1a; 1. 自动化脚本录制和回放功能 Web 自…

口袋参谋:如何通过布局“问大家”,快速提高宝贝转化!

问大家对于中小卖家来说&#xff0c;是非常适合的&#xff0c;因为我们完全可以靠对问大家的布局&#xff0c;提高宝贝的转化率。 问大家的作用 问大家主要是方便买家在购买前&#xff0c;了解商品的一些问题&#xff0c;而作为该不该购买的参考。对于卖家来说&#xff0c;是…

GaussDB新特性Ustore存储引擎介绍

1、 Ustore和Astore存储引擎介绍 Ustore存储引擎&#xff0c;又名In-place Update存储引擎&#xff08;原地更新&#xff09;&#xff0c;是openGauss 内核新增的一种存储模式。此前的版本使用的行存储引擎是Append Update&#xff08;追加更新&#xff09;模式。相比于Append…

亚马逊第二个大语言模型 Olympus 即将上线

据外媒爆料&#xff0c;亚马逊正在训练他的第二个大语言模型——Olympus&#xff0c;很有可能在今年12月份上线。亚马逊计划将Olympus接入在线零售商店、Echo等设备上的Alexa语音助手&#xff0c;并为AWS平台提供新的功能。据说这个大语言模型规模达到2万亿&#xff08;2000B&a…