【公共祖先】二叉树专题

里面涉及多个plus题

  • 前言
  • 1.二叉树的最近公共祖先
  • 2.二叉搜索树的最近公共祖先
  • 3.二叉树的最近公共祖先II
  • 4.二叉树的最近公共祖先III
  • 5.二叉树的最近公共祖先IV

前言

公共祖先这一类题目,难度不大,但是非常实用,也是面试问到概率比较大的一类题目。

为什么实用呢?主要在Git领域:

git pull这个命令默认是使用merge方式将远端别人的修改拉倒本地,如果带上参数,git pull -r,就会使用rebase的方式将远端修改拉倒本地。
这二者最直观的区别就是:merge 方式合并的分支会看到很多「分叉」,而 rebase 方式合并的分支就是一条直线。但无论哪种方式,如果存在冲突,Git 都会检测出来并让你手动解决冲突。

那么问题来了,Git 是如何检测两条分支是否存在冲突的呢?

以 rebase 命令为例,比如下图的情况,我站在 dev 分支执行 git rebase master,然后 dev 就会接到 master 分支之上:
在这里插入图片描述
Git的做法是,首先,找到这两条分支的最近公共祖先 LCA,然后从 master 节点开始,重演 LCA 到 dev 几个 commit 的修改,如果这些修改和 LCA 到 master 的 commit 有冲突,就会提示你手动解决冲突,最后的结果就是把 dev 的分支完全接到 master 上面。

至于Git是如何找到两条不同分支的最近公共祖先,这就是本篇要将的经典算法。

1.二叉树的最近公共祖先

公共祖先有两种情况,情况1是确实有第三个节点为公共祖先,情况2是p或者q是公共祖先。

TreeNode* find(TreeNode* root, int val1, int val2){if(root == nullptr) return nullptr;//前序位置if (root->val == val1 || root->val == val2) {// 情况2// root是不是咱们要找的return root;}// root不是咱要找的,咱就去左边找一找TreeNode* left = find(root->left, val1, val2);// root不是咱要找的,咱就去右边找一找TreeNode* right = find(root->right, val1, val2);//后序位置//汇总左右的情况,看咱找到没//情况1if (left != nullptr && right != nullptr) {// 当前节点是 LCA 节点return root;}return left != nullptr ? left : right;
}

if (root->val == val1 || root->val == val2) 放在后序位置也可以有一样的效果,但是必然会降低效率,后序位置就需要遍历所有节点了。

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

对应二叉搜索树而言,没有必要老老实实遍历,因为可以利用他左小右大的性质,将当前节点的值与 val1 和 val2 作对比即可判断当前节点是不是 LCA

假设 val1 < val2,那么 val1 <= root.val <= val2 则说明当前节点就是 LCA;若 root.val 比 val1 还小,则需要去值更大的右子树寻找 LCA;若 root.val 比 val2 还大,则需要去值更小的左子树寻找 LCA。

class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {// 保证 val1 较小,val2 较大int val1 = min(p->val, q->val);int val2 = max(p->val, q->val);return find(root, val1, val2);}// 在 BST 中寻找 val1 和 val2 的最近公共祖先节点TreeNode* find(TreeNode* root, int val1, int val2) {if (root == nullptr) {return nullptr;}if (root->val > val2) {// 当前节点太大,去左子树找return find(root->left, val1, val2);}if (root->val < val1) {// 当前节点太小,去右子树找return find(root->right, val1, val2);}// val1 <= root->val <= val2// 则当前节点就是最近公共祖先return root;}
};

3.二叉树的最近公共祖先II

和第一题的区别是,如果p或者q不存在树中,要返回null,那么在第一个的find函数中,有这样一段代码:

// 前序位置
if (root.val == val1 || root.val == val2) {// 如果遇到目标值,直接返回return root;
}

现在我们就不能p或者q存在就返回公共节点了,而是需要都存在,那么需要咱定义两个bool类型的变量去记录有没有遍历到p或者q,同时,判断的地方也不要放在前序,为了对二叉树进行完全的搜索,应该把他放在后序:

class Solution {
public:// 用于记录 p 和 q 是否存在于二叉树中bool foundP = false, foundQ = false;TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {TreeNode* res = find(root, p->val, q->val);if (!foundP || !foundQ) {return nullptr;}// p 和 q 都存在二叉树中,才有公共祖先return res;}// 在二叉树中寻找 val1 和 val2 的最近公共祖先节点TreeNode* find(TreeNode* root, int val1, int val2) {if (root == nullptr) {return nullptr;}TreeNode* left = find(root->left, val1, val2);TreeNode* right = find(root->right, val1, val2);// 后序位置,判断当前节点是不是 LCA 节点if (left != nullptr && right != nullptr) {return root;}// 后序位置,判断当前节点是不是目标值if (root->val == val1 || root->val == val2) {// 找到了,记录一下if (root->val == val1) foundP = true;if (root->val == val2) foundQ = true;return root;}return left != nullptr ? left : right;}
};

4.二叉树的最近公共祖先III

这题不太一样,和第一题的区别在于,每个节点都包含其父节点的指针,意思是node* p,p->parent可以找到p的父节点,在这样的背景下找p和q的公共祖先

可以将p和q所在的树枝当做链表
在这里插入图片描述
设两个指针分别从两个给定节点出发,如果两个节点不等,则继续往前一步。如果某个节点到达根节点,则跳到另一个节点最初的位置。最终两个指针一定会相遇在交点处,因为到交点处的路径上面指针走过的路程为L1 + L3 + L2,下面的指针走过的路程为L2 + L3 + L1
(更简单的情况是L1 == L2,则直接找到)

class Solution {
public:Node* lowestCommonAncestor(Node* p, Node * q) {Node *a = p, *b = q;while(a != b){if(a == nullptr) a = q;else a = a->parent;if(b == nullptr) b = p;else b = b->parent;}return a;}
};

5.二叉树的最近公共祖先IV

输入的不是p和q,而是一个包含若干节点的列表nodes,返回这些节点的最近公共祖先

和第一题是一样的

class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, vector<TreeNode*>& nodes) {// 将列表转化成哈希集合,便于判断元素是否存在unordered_set<int> values;for(auto node : nodes) {values.insert(node->val);}return find(root, values);}// 在二叉树中寻找 values 的最近公共祖先节点TreeNode* find(TreeNode* root, unordered_set<int>& values) {if(root == nullptr) {return nullptr;}// 前序位置if(values.find(root->val) != values.end()){return root;}TreeNode* left = find(root->left, values);TreeNode* right = find(root->right, values);// 后序位置,已经知道左右子树是否存在目标值if (left != nullptr && right != nullptr) {// 当前节点是 LCA 节点return root;}return left != nullptr ? left : right;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

飞牛NAS未识别到网卡

最新都说国产免费的飞牛NAS非常好用&#xff0c;再也不用搞黑群辉了。 以前也没有搞过NAS&#xff0c;刚好借着这个机会学习一下NAS产品。 在虚拟机上安装&#xff0c;安装还挺顺利&#xff0c;就打算在买来的 也试试&#xff0c;结果系统都安装成功了&#xff0c;但是提示“…

进程通信——管道

文章目录 1. 管道简介2. 无名管道2.1 简介2.2 系统调用2.2.1 无名管道的创建和关闭2.2.2 pipe()2.2.3 无名管道读写说明2.2.4 代码示例 3. 命名管道3.1 简介3.2 mkfifo3.3 对于读进程3.4 对于写进程3.5 代码示例3.5.1 写管道3.5.2 读管道 1. 管道简介 管道是Linux中进程间通信…

物理环境检测及绘制

来解决连续跳跃这个问题&#xff0c;只有在地面上才可以执行跳跃 为了实现这个物理检测&#xff0c;我们需要单独写一个代码&#xff0c;因为除了人物需要检测周围的物理环境以外&#xff0c;我们的敌人也需要检测周围的物理环境&#xff0c;敌人撞墙需要返回继续走&#xff0…

《15分钟轻松学Go》教程目录

在AI快速发展的时代&#xff0c;学习Go语言依然很有用。Go语言擅长处理高并发任务&#xff0c;也就是说可以同时处理很多请求&#xff0c;这对于需要快速响应的AI服务非常重要。另外&#xff0c;Go适合用来处理和传输大量数据&#xff0c;非常适合机器学习模型的数据预处理。 …

linux提权【笔记总结】

文章目录 信息收集通过命令收集信息内核&#xff0c;操作系统&#xff0c;设备信息等用户信息环境信息进程与服务安装的软件服务与插件计划任务查看是否存在明文密码查看与主机的通信信息查看日志信息 通过脚本收集信息LinEnum脚本介绍复现 Linuxprivchecker复现 linux-exploit…

POMO:强化学习的多个最优策略优化(2020)(完)

文章目录 Abstract1 Introduction2 Related work3 Motivation4 多最优策略优化(POMO)4.1 从多个起始节点进行探索4.2 策略梯度的共享基线4.3 用于推理的多个贪婪轨迹5 Experiments5.1 Traveling salesman problem5.2 带容量限制得车辆路径问题5.3 0-1背包问题6 ConclusionAbs…

题目:小金鱼吐泡泡

解题思路&#xff1a; 用栈模拟&#xff0c;创建2个栈&#xff0c;a&#xff1a;字符串的栈&#xff0c;栈顶为s末尾&#xff1b;q&#xff1a;答案栈&#xff0c;与a顶元素互动做相应操作。 陷入的误区&#xff1a;认为可以两个方向可以随意消&#xff0c;但不同方向消得到的结…

AIGC时代 | 揭秘大型语言模型微调:11种高效方法助力模型升级

导读&#xff1a;大型预训练模型是一种在大规模语料库上预先训练的深度学习模型&#xff0c;它们可以通过在大量无标注数据上进行训练来学习通用语言表示&#xff0c;并在各种下游任务中进行微调和迁移。随着模型参数规模的扩大&#xff0c;微调和推理阶段的资源消耗也在增加。…

【H2O2|全栈】JS入门知识(二)

目录 JS 前言 准备工作 运算符 算数运算符 比较运算符 自增、自减运算符 逻辑运算符 运算符的优先级 分支语句 if-else语句 switch语句 三元表达式 结束语 JS 前言 本系列博客主要分享JavaScript的基础语法知识&#xff0c;本期为第二期&#xff0c;包含一些简…

c++应用网络编程之十一Linux下的epoll模式基础

一、epoll模式 在前面分析了select和poll两种IO多路复用的模式&#xff0c;但总体给人的感觉有一种力不从心的感觉。尤其是刚刚接触底层网络开发的程序员&#xff0c;被很多双十一千万并发&#xff0c;游戏百万并发等等已经给唬的一楞一楞的。一听说只支持一两千个并发&#x…

阿里Dataworks使用循环节点和赋值节点完成对mongodb分表数据同步

背景 需求将MongoDB数据入仓MaxCompute 环境说明 MongoDB 100个Collections&#xff1a;orders_1、orders_2、…、orders_100 前期准备 1、MongoDB数据源配置 需要先保证DW和MongoDB网络是能够联通的&#xff0c;需要现在集成任务中配置MongoDB的数据源信息。 具体可以查…

Python OpenCV精讲系列 - 三维重建深入理解(十七)

&#x1f496;&#x1f496;⚡️⚡️专栏&#xff1a;Python OpenCV精讲⚡️⚡️&#x1f496;&#x1f496; 本专栏聚焦于Python结合OpenCV库进行计算机视觉开发的专业教程。通过系统化的课程设计&#xff0c;从基础概念入手&#xff0c;逐步深入到图像处理、特征检测、物体识…

AD9361 在低至 1MHz 的频率下运行

AD9361 在低至 1MHz 的频率下运行 AD -FREQCVT1-EBZ是包含AD9361的FMCOMMS3/4/5板的附加板。虽然完整的芯片级设计包可在此 RF 收发器的ADI产品页面上找到&#xff0c;但有关此卡的信息及其使用方法、围绕它的设计包以及可使其工作的软件可在此处找到。 AD-FREQCVT1-EBZ 模块…

无人机之放电速率篇

无人机的放电速率是指电池在一定时间内放出其储存电能的能力&#xff0c;这一参数对无人机的飞行时间、性能以及安全性都有重要影响。 一、放电速率的表示方法 放电速率通常用C数来表示。C数越大&#xff0c;表示放电速率越快。例如&#xff0c;一个2C的电池可以在1/2小时内放…

储能电源自动化测试系统中不同硬件电路设计对测试结果有哪些影响?-纳米软件

随着能源领域的不断发展&#xff0c;储能电源在各个领域的应用越来越广泛。为了确保储能电源的性能和可靠性&#xff0c;自动化测试系统的重要性日益凸显。其中&#xff0c;硬件电路设计是自动化测试系统的关键组成部分&#xff0c;不同的硬件电路设计会对测试结果产生不同的影…

程序报错:ModuleNotFoundError: No module named ‘code.utils‘; ‘code‘ is not a package

程序报错内容&#xff1a; Traceback (most recent call last): File "code/nli_inference/veracity_prediction.py", line 10, in <module> from code.utils.data_loader import read_json ModuleNotFoundError: No module named code.utils; code is …

Linux运维_Apache更改默认网站目录

1.首先创建目录 并且在目录下新建测试文件 index.html mkdir -p /home/test/ap_web 直接wget 百度官网 wget www.baidu.com 2.编辑配置文件 /etc/apache2/sites-available/000-default.conf(找到 DocumentRoot)更改为刚刚创建的目录 接着在添加 最终文件: 3.给文件 添加属…

面试题:Redis(五)

1. 面试题 面试问 记录对集合中的数据进行统计 在移动应用中&#xff0c;需要统计每天的新增用户数和第2天的留存用户数&#xff1b; 在电商网站的商品评论中&#xff0c;需要统计评论列表中的最新评论&#xff1b; 在签到打卡中&#xff0c;需要统计一个月内连续打卡的用户数&…

【AI大模型】羊驼大模型详解_零基础入门到精通,看完这篇就足够了~

LLaMa系列模型 羊驼模型&#xff08;鼻祖是LLaMa模型&#xff0c;Facebook公司开源模型&#xff09;&#xff1a;即将成为大模型的安卓&#xff0c;国内95%的大模型都是羊驼套壳。GPT系列&#xff08;OpenAI公司&#xff09;&#xff1a;相当于大模型的iOS&#xff08;不开源&…

鸿蒙OS启动流程

启动流程(基于openharmony4.1) 系统上电加载内核后&#xff0c;按照以下流程完成系统各个服务和应用的启动&#xff1a; 内核加载init进程&#xff0c;一般在bootloader启动内核时通过设置内核的cmdline来指定init的位置。init进程启动后&#xff0c;会挂载tmpfs&#xff0c;…