【Leetcode】二叉树的最近公共祖先,二叉搜索树转换成排好序的双向链表,前序遍历与中序遍历构造二叉树

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

链接

二叉树的最近公共祖先

题目再现

 『Ⅰ』思路一:转换成相交链表问题

 观察上图,节点1和节点4的最近公共祖先是3,这是不是很像相交链表的问题,关于相交链表,曾经我在另一篇文章里写到过,读者可以参考:反转链表 合并链表 相交链表

但是要转换成相交链表,就要从后向前遍历,如果节点中还存在一个指针,指向父节点就好了,这种结构其实叫三叉链结构

 但是这题给我们的只是一个普通的二叉树,没有三叉链,那该怎么办呢?

那么就转换为第二种思路:寻找节点的祖先路径

『Ⅱ』思路二:寻找节点的祖先路径

  我们可以把要找的两个节点的路径找出来,然后存到栈里,这样把两个节点的祖先路径找出来后,就可以转换成链表相交问题了。

关于该怎么入栈:

我们先让节点入栈,然后判断它是否等于我们要找的节点,如果是,则返回true;如果不是,则

                1.如果左节点不为空,返回true

                2.如果右节点不为空,返回true

                3.如果左右节点都为空,则pop掉栈顶的元素,返回false

完整代码:

class Solution {
public:bool findpath(TreeNode*cur,TreeNode*x,stack<TreeNode*>&path)  //注意这里要传引用{if(cur==nullptr)return false;path.push(cur);if(cur==x)return true;if(findpath(cur->left,x,path))return true;if(findpath(cur->right,x,path))return true;path.pop();return false;}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {stack<TreeNode*> ppath;stack<TreeNode*> qpath;findpath(root,p,ppath);findpath(root,q,qpath);while(ppath.size()>qpath.size())  //使两个栈一样长{ppath.pop();}while(ppath.size()<qpath.size()){qpath.pop();}while(ppath.top()!=qpath.top())   //从栈顶开始,寻找第一个相同的数据{ppath.pop();qpath.pop();}return ppath.top();}
};

         

可以看到,这种方法效率使非常高的,它的时间复杂度是O(N);

 『Ⅲ』思路三:暴力查找

其实当两个节点分别在左树和右树时,它们最近的公共祖先就是根节点,如果不在树两边,而是都在左树,或是都在右树,那么就可以转化成子问题,递归解决

如下图:

 注意,如果有一个节点恰好是根节点,那么这个节点就是最近的公共祖先,也是说一个节点的祖先也算它自己

如下图:

 那么该怎么判断节点是在左树还是右树呢?

我们可以定义四个布尔变量,分别是:pinleft(p在左树)   pinright(p在右树)

                                                             qinleft (q在左树 ) qinright(q在右树)

哪个布尔值为真就表明这个节点在哪边

完整代码:

class Solution {
public:bool find(TreeNode*cur,TreeNode*x){if(cur==nullptr)return false;return cur==x||find(cur->left,x)||find(cur->right,x);}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode*q)  {if(root==nullptr)return nullptr;if(root==p||root==q)  //某一个节点为根return root;bool pinleft,pinright;bool qinleft,qinright;pinleft=find(root->left,p);   //去左树寻找p节点pinright=!pinleft;qinleft=find(root->left,q);   //去左树寻找q节点qinright=!qinleft;if(pinleft&&qinleft)   //都在左树转化成子问题return lowestCommonAncestor(root->left,p,q);else if(pinright&&qinright)    //都在右树转化成子问题return lowestCommonAncestor(root->right,p,q);else    //分别在左树和右树return root;}
};

可以看到,这个算法的效率是很差的,它的时间复杂度是O(N^2)


二.二叉搜索树转换成排好序的双向链表 

链接

二叉搜索树转换成排好序的双向链表

题目再现

 解法

根据题意,原二叉搜索树的左指针就是双链表的前驱指针,右指针就是双链表的后继指针

而且本题还要求空间复杂度是O(1),也就是说不能额外开空间,其实要是能额外开空间,那么这题就非常简单了。

我们知道,二叉搜索树的中序遍历结果是升序列,这恰好满足了题目排好序的要求;

那要怎么在原树上操作,使它转换成双链表呢?

举个例子:

对于我们过去(prev)的事,现在(cur)的我们肯定是一清二楚的,而且是可以确定的,但未来(next)的事并不能确定;

但如果我们是从未来穿越回现在的,那么穿越回来的我们,就可以确定未来的事。所以说过去(prev)的未来(next)就是现在(cur)

回到题目,所以cur的左指针(left)就是双链表的前驱(prev),prev的右指针就是后继(next),然后再更新一下prev即可

完整代码:

class Solution {
public:void InOrder(TreeNode*cur,TreeNode*&prev)   //注意要传引用{if(cur==nullptr)return;InOrder(cur->left,prev);cur->left=prev;   //cur的左指针就是previf(prev)   //注意判断prev是否为空prev->right=cur;   //prev的右指针就是curprev=cur;  //更新prevInOrder(cur->right,prev);}TreeNode* Convert(TreeNode* pRootOfTree) {if(pRootOfTree==nullptr)return nullptr;TreeNode*prev=nullptr;   //定义一个前驱指针InOrder(pRootOfTree,prev);   //中序遍历TreeNode*head=pRootOfTree;while(head->left)   //最左边的节点即为双链表的头{head=head->left;}return head;}
};

三.根据一棵树的前序遍历与中序遍历构造二叉树

链接

根据一棵树的前序序列与中序序列构建二叉树

题目再现

 解法

众所周知,前序遍历的顺序为:根  左  右

                  中序遍历的顺序为:左  根  右

所以根据前序序列就可以确定根,确定了根后就可以分成左子树和右子树;

确定好根后,根据中序序列就可以分割左子树和右子树的区间,然后构建树

而左子树或是右子树也有根,这样就可以转化成子问题,递归实现,但要注意,前序序列中的每个数只能使用一次。

 完整代码:

class Solution {
public://注意这个prei用于遍历前序序列数组,因为每个数只能用一次,所以要传引用TreeNode*_build(vector<int>& preorder, vector<int>& inorder,int &prei,int inbegin,int inend){if(inbegin>inend)   //当区间不存在时返回空指针return nullptr;TreeNode*preroot=new TreeNode(preorder[prei]);int rooti=inbegin;     //找根在中序序列中的位置while(rooti<=inend){if(preorder[prei]==inorder[rooti])   //找到后跳出循环break;rooti++;}prei++;   //本次确定好根后,prei++找下一个根//分割区间,递归构建//[inbegin,rooti-1] rooti [rooti+1,inend]preroot->left=_build(preorder,inorder,prei,inbegin,rooti-1);preroot->right=_build(preorder,inorder,prei,rooti+1,inend);return preroot;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int i=0;return _build(preorder,inorder,i,0,inorder.size()-1);}
};

🐬🤖本篇文章到此就结束了, 若有错误或是建议的话,欢迎小伙伴们指出;🕊️👻

😄😆希望小伙伴们能支持支持博主啊,你们的支持对我很重要哦;🥰🤩

😍😁谢谢你的阅读。😸😼

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

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

相关文章

面试热题100(二叉树的右视图)

给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 树这类问题用的最多的就是递归&#xff0c;因为树具有天然的递归结构&#xff1a; 我们来分析一下题目&#xff0c;给定一棵树根结…

pytorch中torch.einsum函数的详细计算过程图解

第一次见到 rel_h torch.einsum(“bhwc,hkc->bhwk”, r_q, Rh)这行代码时&#xff0c;属实是懵了&#xff0c;网上找了很多博主的介绍&#xff0c;但都没有详细的说明函数内部的计算过程&#xff0c;看得我是一头雾水&#xff0c;只知道计算结果的维度是如何变化的&#xf…

SpringCloud之微服务API网关Gateway介绍

文章目录 1 微服务API网关Gateway1.1 网关简介1.2 Spring Cloud Gateway介绍1.3 Gateway特性1.4 Gateway核心概念1.4.1 路由1.4.1.1 定义1.4.1.2 动态路由 1.4.2 断言1.4.2.1 默认断言1.4.2.2 自定义Predicate 1.4.3 过滤器1.4.3.1 默认过滤器1.4.3.2 自定义Filter&#xff08;…

C++ 用vector创建数组对象

C标准库提供了被封装的动态数组——vector&#xff0c;而且这种被封装的数组可以具有各种类型&#xff0c;这就使我们免去了一些重复性工作。 vector不是一类&#xff0c;而是一个类模板。 1. vector定义动态数组的形式为 vector<元素类型>数组对象名(数组长度);尖括号…

【C语言初阶】指针篇—下

目录 4. 指针运算4.1 指针-整数4.2 指针-指针4.3 指针的关系运算 5. 指针和数组6. 二级指针7. 指针数组 C语言初阶—指针上 点击跳转 4. 指针运算 指针 整数指针-指针指针的关系运算 4.1 指针整数 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>int main() {in…

关于Monkey稳定性测试,这是我看到最详细的文章

通过随机点击屏幕一段时间&#xff0c;看看app会不会崩溃&#xff0c;能不能维持正常运行&#xff0c;这就是稳定性测试。 01、Monkey是什么 Monkey测试是Android平台自动化测试的一种手段&#xff0c;通过Monkey程序模拟用户触摸屏幕、滑动Trackball、按键等操作来对设备上的…

2023 电赛E题--可能会出现的问题以及解决方法

2023年电赛E题报告模板&#xff08;K210版&#xff09;--可直接使用 本文链接&#xff1a;2023年电赛E题报告模板&#xff08;K210版&#xff09;--可直接使用_皓悦编程记的博客-CSDN博客 解决激光笔在黑色区域无法识别 本文链接&#xff1a; 2023 电赛 E 题 激光笔识别有误-…

获取SQL语句表名,判断DDL类型

1.在maven中引入jsqlparser依赖 <!--sql语句解析--><dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.4</version></dependency>2.解析SQL语句具体代码 此代码解析…

AI Chat 设计模式:12. 享元模式

本文是该系列的第十二篇&#xff0c;采用问答式的方式展开&#xff0c;问题由我提出&#xff0c;答案由 Chat AI 作出&#xff0c;灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 给我介绍一下享元模式A.1Q.2 也就是说&#xff0c;其实共享的是对象的内部状态&…

【unity之IMGUI实践】游戏结束流程封装实现【七】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

LNMP搭建及论坛搭建

一、LNMP LNMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整套系统和相关软件&#xff0c; 能够提供动态Web站点服务及其应用开发环境。LNMP是一个缩写词&#xff0c;具体包括Linux操作系统、nginx网站服务器、MySQL数据库服务器、PHP&#xff08;或…

【无标题】uniapp引入萤石云 真机无法运行 踩坑集合

Uniapp 接入萤石云 踩坑 1.先用了 UIKit Javascript 就是在 pc端 那套流程 npm install ezuikit-jsimport EZUIKit from ezuikit-js;这套流程貌似只适用于pc端&#xff0c;我在接入uniapp的时候没看官网 以为都是一套流程&#xff0c;然后就在uniapp中也来了这一套&#xff0…

python与深度学习(十五):CNN和宝可梦模型

目录 1. 说明2. 宝可梦模型2.1 导入相关库2.2 建立模型2.3 模型编译2.4 数据生成器2.5 模型训练2.6 模型保存 3. 宝可梦的CNN模型可视化结果图4. 完整代码5. 宝可梦的迁移学习 1. 说明 本篇文章是CNN的另外一个例子&#xff0c;宝可梦模型&#xff0c;是自制数据集的例子。之前…

快速文件传输常见问题

我们所处的世界充斥着各种信息&#xff0c;能够迅速获得正确的数据往往是企业成功的关键因素。将文件从A点移动到B点需要考虑很多问题&#xff0c;但是当涉及需要在最短时间内送达全球各地收件人的大型关键任务文件时&#xff0c;就不能再使用Dropbox和 Google Drive 等方案了。…

互联网的发展

目录 1.什么是互联网 2.互联网的发展历史 3.中国互联网的发展历程 4.互联网对人们生活的影响 5.互联网给人类带来了哪些负面影响 1.什么是互联网 互联网&#xff08;Internet&#xff09;是一个全球性的计算机网络系统&#xff0c;它连接了数十亿台计算机和其他设备。它是由…

il汇编整数相加

在这里尝试了IL汇编字符串连接&#xff1b; IL汇编字符串连接_bcbobo21cn的博客-CSDN博客 下面来看一下IL汇编整数相加&#xff1b; 大概的看一下一些资料&#xff0c;下面语句&#xff0c; ldc.i4 20 ldc.i4 30 add 看上去像是&#xff0c;装载整数20到一个类似于…

无涯教程-Perl - 面向对象

Perl中的面向对象概念很大程度上基于引用以及匿名数组和哈希。让我们开始学习面向对象Perl的基本概念。 定义类 在Perl中定义一个类非常简单。类以最简单的形式对应于Perl软件包。要在Perl中创建一个类&#xff0c;我们首先构建一个包。 Perl软件包在Perl程序中提供了一个单…

EditPlus取消自动.bak备份

Tools->Preferences->File 将√取消

Vulnhub靶机DC-2 writeup

靶机介绍 靶机介绍&#xff1a;https : //download.vulnhub.com/dc/DC-2.zip ​ 信息搜集 获取IP地址 扫描靶机的IP的方法 1. nmap -sP 192.168.142.0/24 #nmap进行ping扫描发现存活主机 2. arp-scan -l #基于ARP发现内网存活主机 3. netdiscover -r 192.168.142.0/24 -…

qt富文本编辑基本知识(QTextBlockFormat、QTextListFormat)

可以参考该文章&#xff1a;QTextBlockFormat、QTextListFormat - 程序员大本营 核心知识如下&#xff1a; 如果想开发一个富文本编辑器&#xff08;html&#xff0c;markdown等常见格式&#xff09;&#xff0c;Qt已经为用户完成了几乎所有与编辑有关的具体工作&#xff0c;…