算法力扣刷题记录 五十七【236. 二叉树的最近公共祖先】和【235. 二叉搜索树的最近公共祖先】

news2024/9/21 18:54:38

前言

公共祖先解决。二叉树和二叉搜索树条件下的最近公共祖先。
二叉树篇继续。


一、【236. 二叉树的最近公共祖先】题目阅读

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

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:
在这里插入图片描述

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

示例 2:
在这里插入图片描述

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

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

提示:

树中节点数目在范围 [2, 10^5] 内。
-10^9 <= Node.val <= 10^9
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。

二、【236. 二叉树的最近公共祖先】题目解答

弯路(第一次做题时,可能的想法)

  1. 二叉树习惯用递归法解决问题,那么需要确定遍历顺序重复执行的递归逻辑
  2. 本题是二叉树,需要求最近公共祖先,那么父节点应该是最后遍历。所以尝试遍历顺序:后序——左右中。
  3. 如何找到统一执行的规律呢?回到中间节点需要处理什么逻辑?
  • 受前几节二叉搜索树中序遍历整合到数组中,那么此处遍历结果数组得到之后,看p,q哪个在前,选择在前面的p或者q?可是无法区分自己是祖先的情况。(不正确)

正确思路获取

参考思路链接

  1. 确定后序遍历,因为需要从节点p,q向上退出父节点,所以中间节点最后处理。(左右中)
  2. 返回中间节点什么信息?判断左子树中是否存在p或q,判断右子树中是否存在p或q:
  • 如果左子树有p,右子树有q;左子树有q,右子树有p。此时返回中间节点(最近公共祖先)。

  • 如果一边子树为空,另一边子树有pq之一。此时返回left或者right,即子树返回的节点。注意:这里不能返回中间节点。会把最近公共祖先丢掉。
    在这里插入图片描述

  • 如果两边子树都为空,说明该中间节点的左右子树不包含pq。向上返回空。

  1. 所以递归函数返回值:TreeNode* 类型;
  2. 递归函数终止条件:如果cur是空,return空。如果cur->val是pq,return cur。
  • 两个终止条件:第一个空判断可以容易想到。
  • 如果第二个终止条件没有想到:影响吗?可以拯救:在中间逻辑先判断该节点是否为p或q,如果是,那么return cur;如果不是,走第2.点的逻辑。
  • 所以第二个终止条件放到中间节点之前,更简单。
  1. 总结:左右中:先看左右子树能向中间节点返回什么信息——空节点说明不包含p和q;非空节点说明有p或有q或有p和q。注意,中间节点应该真正返回什么,有的是cur(自身),有的是left或right.

代码实现【二叉树的最近公共祖先+递归法】

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root) return nullptr;
        if(root->val == q->val || root->val == p->val) return root;

        //左
        TreeNode* left = lowestCommonAncestor(root->left,p,q);
        //右
        TreeNode* right = lowestCommonAncestor(root->right,p,q);
        //中节点
        if(!left && !right) return nullptr;//说明该子树没有p也没有q
        else if(!left && right) return right;//一定要返回right。而不是root
        else if(!right && left) return left;//一定要返回left。而不是root。
        else return root;
    }
};

补充说明【细节】

补充思路中没提及的情况2:自身是祖先——以上代码在哪一处包含

  1. p和q不是包含关系,情况1:肯定是某个中间节点的两边子树;
  2. p和q是包含关系,无论谁包含谁——情况2:如示例2.在某个节点的同一个子树内。if(root->val == q->val || root->val == p->val) return root;将包含者向上返回。(不用深入找q了。)
    在这里插入图片描述

拯救:没想到终止条件2时

  • 可以这样想:先看自己是不是目标,如果是,返回自己。如果不是,看左右子树有没有包含。
  • 看left和right之前先确定自己不是目标。else return root。
  • 所以把终止条件2放到上面更好一点。
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root) return nullptr;
        //没想到在这里直接终止。
        //if(root->val == q->val || root->val == p->val) return root;

        //左
        TreeNode* left = lowestCommonAncestor(root->left,p,q);
        //右
        TreeNode* right = lowestCommonAncestor(root->right,p,q);
        //中节点
        if(root->val == p->val || root->val == q->val){
            return root;
        }else{
            if(!left && !right) return nullptr;//说明该子树没有p也没有q
            else if(!left && right) return right;//一定要返回right。而不是root
            else if(!right && left) return left;//一定要返回left。而不是root。
            else return root;
        }
    }
};

理解提示给节点值不重复、p和q不相等、p 和 q 均存在于给定的二叉树中

  • 这个提示有用。值不相等,说明p是唯一的节点,没有p1,p2,p3……,return的一定是节点p;q同理。
  • p和q不相等,且都在二叉树中,说明:
    • 情况一:p和q非包含关系。一左一右,说明left和right一个遇到p,一个遇到q。
    • 情况二:p和q包含关系。说明left或right先遇到其中之一。

三、【235. 二叉搜索树的最近公共祖先】题目阅读

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

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
在这里插入图片描述

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

示例 2:

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

说明:

所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉搜索树中。

四、【235. 二叉搜索树的最近公共祖先】题目解答

4.1 尝试解答

4.1.1思路【个人】

如果当作普通二叉树,那么上一道题肯定能解决。那么此处就要利用二叉搜索树的性质。

  1. 二叉搜索树最大的特点:左子树 < 中间节点 < 右子树,数值能有序整理这可以给搜索指明方向,就不用搜索整个树
  2. 节点p和节点q,假设p->val < q->val,始终坚持这个原则,如果不是,就要换数据。
  3. 大小关系:
  • 如果cur->val > q->val,说明应该遍历左子树,右子树不用遍历,得到左子树的返回之后,直接return left。
  • 如果cur->val < p->val ,说明应该遍历右子树,左子树不用遍历,得到右子树的返回之后,直接return right。
  • 如果p->val < cur->val < q->val,当前节点的值在中间,说明当前节点就是公共祖先
  • 可以画图辅助理解。如下:
    在这里插入图片描述
  1. 递归函数终止条件:上面已经说了两点终止条件。自然再加上cur为空的情况。
  2. 递归函数返回值:返回节点类型。

4.1.2代码实现【递归】

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Solution {
public:
    void swap(TreeNode*& p, TreeNode*& q){
        if(p->val > q->val){//设定q的值大于p的值。
            TreeNode* temp = p;
            p = q;
            q = temp;
        }
        return;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        //先保证p的值小于q的值
        swap(p,q);
        //终止条件1:空节点返回空
        if(!root) return nullptr;
        //终止条件2:中间节点的值在p和q中间,或自身祖先。
        if( (p->val < root->val && root->val < q->val) || root->val == p->val || root->val == q->val) 
            return root;

        if(root->val > q->val){
            TreeNode* left = lowestCommonAncestor(root->left,p,q);//进到左子树
            return left;//此处会直接返回。没有遍历整个树。
        }else if(root->val < p->val){
            TreeNode* right = lowestCommonAncestor(root->right,p,q);//进到右子树
            return right;
        }
        return nullptr;//走不到这。
    }
};

4.2 【235. 二叉搜索树的最近公共祖先】参考学习

参考学习链接

学习内容

  1. 思路大体一致,但是代码还有改进点:
  • 判断条件的处理上:中间root->val比p和q的值都大,那么遍历左子树;中间root->val比p和q的值都小,那么遍历右子树。剩下就是root->val在中间,直接返回即可。
  • 因此:不需要swap(p,q) 来始终保持q的值比p大;但是第一次做,这是最直观,方便分析的思路。
  1. 所以:代码改进【递归法】
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        //终止条件:空节点返回空
        if(!root) return nullptr;

        if(root->val > q->val && root->val > p->val){
            TreeNode* left = lowestCommonAncestor(root->left,p,q);//进到左子树
            //没有加left为空的判断,因为题目说p和q存在。严格来说加上更通用。
            if(left) return left;//此处会直接返回。没有遍历整个树。
        }else if(root->val < p->val && root-=>val < q->val){
            TreeNode* right = lowestCommonAncestor(root->right,p,q);//进到右子树
            if(right) return right;
        }
        //中
        return root;//剩余情况。
    }
};
  1. 迭代法:思路肯定一样。尝试实现下:
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        queue<TreeNode*> que;
        if(!root) return nullptr;
        que.push(root);
        while(!que.empty()){
            TreeNode* cur = que.front();que.pop();
            if(cur->val > p->val && cur->val > q->val && cur->left){
                que.push(cur->left);
            }else if(cur->val < p->val && cur->val < q->val && cur->right){
                que.push(cur->right);
            }else return cur;
        }
        return nullptr;//能找到在上一行会直接return,不会走到这一行。
    }
};

对比参考代码:上面还是有点复杂——用了队列,不过这是迭代模版,肯定能实现。
参考给出:直接用root指针交接下一棒。


总结

在这里插入图片描述
(欢迎指正,转载标明出处)

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

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

相关文章

Spring Bean介绍

目录 1.什么是bean 2.获取bean 3.bean的作用域 4.第三方bean 5.Bean的生命周期 6.Bean的种类 7.为什么使用Bean&#xff1f; 1.什么是bean Bean是Java世界中的一种组件&#xff0c;用于封装数据和逻辑&#xff0c;以便在应用程序中重用和维护。它不仅可以装在数据&#x…

Redis哨兵模式实践

本次环境为Centos7.6&#xff0c;redis-7.0.4 1&#xff1a;主备模式&#xff1a;即主节点的数据自动同步到从节点&#xff0c;但当主节点挂了&#xff0c;从节点需要手动设置为主节点&#xff0c;比较麻烦。 2&#xff1a;哨兵模式&#xff1a;当主节点挂了&#xff0c;自动投…

PCL-基于SAC_IA和NDT结合的点云配准算法

一、原理概述1.点云配准流程图2.快速点特征直方图FPFH3.采样一致性SAC_IA粗配准4.正态分布变换NDT精配准 二、实验代码三、实验结果四、总结五、参考 一、原理概述 1.点云配准流程图 2.快速点特征直方图FPFH 快速点特征直方图&#xff08;Fast Point Feature Histogram&#…

Oracle SQL:了解执行计划和性能调优

查询优化类似于制作完美食谱的艺术——它需要对成分&#xff08;数据&#xff09;、厨房&#xff08;数据库系统&#xff09;和使用的技术&#xff08;查询优化器&#xff09;有深入的了解。每个数据库系统都有自己的处理和运行 SQL 查询的方式&#xff0c;“解释”计划向我们展…

Mysql注意事项(一)

Mysql注意事项&#xff08;一&#xff09; 最近回顾了一下MySQL&#xff0c;发现了一些MySQL需要注意的事项&#xff0c;同时也作为学习笔记&#xff0c;记录下来。–2020年05月13日 1、通配符* 检索所有的列。 不建议使用 通常&#xff0c;除非你确定需要表中的每个列&am…

每日刷题记录(codetop版)

7.21 7.22 7.23 复习7.21和7.22

每日OJ_牛客DD1 连续最大和

目录 牛客DD1 连续最大和 解析代码 牛客DD1 连续最大和 连续最大和_牛客题霸_牛客网 解析代码 本题是一个经典的动规问题&#xff0c;简称dp问题&#xff0c;但这个问题是非常简单的dp问题&#xff0c;而且经常会考察&#xff0c;所以一定要把这个题做会。本题题意很简单&am…

探寻安全新时代:叉车AI智能影像防撞系统,守护生命之光

在繁忙的工业现场&#xff0c;叉车司机常常面临着视线受阻的困境&#xff0c;那些被货物遮挡的盲区&#xff0c;仿佛隐藏着无法预知的危险。然而&#xff0c;这样的隐患在一次惨痛的事故中暴露无遗&#xff0c;一名无辜的行人因叉车司机的视线受阻而不幸被撞身亡。这起悲剧让我…

机械设计基础B(学习笔记)

绪论 机构&#xff1a;是一些具备各自特点的和具有确定的相对运动的基本组合的统称。 组成机构的各个相对运动部分称为构件。构件作为运动单元&#xff0c;它可以是单一的整体&#xff0c;也可以是由几个最基本的事物&#xff08;通常称为零件&#xff09;组成的刚性结构。 构件…

python·数据分析基础知识

numpy 一个数值计算包 python列表与numpy矩阵区别 python中修改列表元素和列表相加 for循环 &#xff1a;[x1 for x in a] 多个元素需要用zip捆绑&#xff1a;[xy for(x,y) in zip(a,b)] numpy矩阵自动进行相应元素计算 np.array()1各元素1 ab各元素相加 a*b矩阵相乘或者是…

爬虫学习4:爬取王者荣耀技能信息

爬虫&#xff1a;爬取王者荣耀技能信息&#xff08;代码和代码流程&#xff09; 代码 # 王者荣耀英雄信息获取 import time from selenium import webdriver from selenium.webdriver.common.by import By if __name__ __main__:fp open("./honorKing.txt", "…

C++的UI框架和开源项目介绍

文章目录 1.QT2.wxWidgets3.Dear ImGui 1.QT QT的开源项目&#xff1a;QGIS&#xff08;地理信息系统&#xff09; https://github.com/qgis/QGIS?tabreadme-ov-file 2.wxWidgets wxWidgets的开源项目&#xff1a;filezilla https://svn.filezilla-project.org/svn/ wxWidg…

Matplotlib折线图绘制秘籍:让你的数据线条比过山车还刺激!

1. Matplotlib_折线图 折线图&#xff08;line chart&#xff09;是我们日常工作中经常使用的一种图表&#xff0c;它可以直观的反应数据的变化趋势 # 导包 import numpy as np import pandas as pd import matplotlib.pyplot as plt# 如果浏览器不显示图片&#xff0c;就需要…

面试场景题系列--(1)如果系统的 QPS 突然提升 10 倍该怎么设计?--xunznux

1. 如果系统的 QPS 突然提升 10 倍该怎么设计&#xff1f; 1.1 硬件的扩展微服务的拆分 如果所有的业务包括交易系统、会员信息、库存、商品等等都夹杂在一起&#xff0c;当流量一旦起来之后&#xff0c;单体架构的问题就暴露出来了&#xff0c;机器挂了所有的业务就全部无法…

kafka集群搭建-使用zookeeper

1.环境准备&#xff1a; 使用如下3台主机搭建zookeeper集群&#xff0c;由于默认的9092客户端连接端口不在本次使用的云服务器开放端口范围内&#xff0c;故端口改为了8093。 172.2.1.69:8093 172.2.1.70:8093 172.2.1.71:8093 2.下载地址 去官网下载&#xff0c;或者使用如…

达梦索引组织表和堆表

达梦数据库默认创建的是索引组织表&#xff0c;‌而Oracle数据库默认创建的是堆表。‌这两种表类型的区别主要体现在数据存储和组织方式上&#xff1a; 索引组织表(‌Index Organized Table, IOT)&#xff1a;‌ 索引组织表‌有且仅有一个聚簇索引键。索引组织表也称“普通表”…

UGUI优化篇--UGUI合批

UGUI合批 UGUI合批规则概述UGUI性能查看工具合批部分的特殊例子一个白色image、蓝色image覆盖了Text&#xff0c;白色image和Text哪个先渲染 Mask合批Mask为什么会产生两个drawcallMask为什么不能合批Mask注意要点 RectMask2D为什么RecMask2D比Mask性能更好主要代码RectMask2D注…

【笔记】学习记录

2024年7月23日 1.图的5中存储方式 2.二叉树的先序&#xff0c;中序&#xff0c;后序遍历。 学了图的存储方式之后&#xff0c;二叉树好像就是小菜一碟一样。注意一下名词的顺序就可以了。 所谓先中后序&#xff0c;就是先根&#xff0c;中根&#xff0c;后根的差别。没有其…

数据库练习-3

查询要求&#xff1a; 查询代码&#xff1a;

Apache2服务介绍

apache2 安装使用配置web访问配置虚拟主机配置代理正向代理反向代理 官网 互联网上排名第一的 HTTP 服务器&#xff0c;Apache HTTP 服务器项目致力于开发和维护适用于现代操作系统&#xff08;包括 UNIX 和 Windows&#xff09;的开源 HTTP 服务器。该项目的目标是提供安全、…