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

news2024/11/13 23:25:11

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

链接

二叉树的最近公共祖先

题目再现

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

 观察上图,节点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的左指针就是prev
		if(prev)   //注意判断prev是否为空
			prev->right=cur;   //prev的右指针就是cur
		prev=cur;  //更新prev
		InOrder(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.coloradmin.cn/o/834559.html

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

相关文章

WPS Office AI实战:智能表单,信息收集神器

前面我们已经介绍了WPS里常用的文字、表格、演示文稿等等&#xff0c;在WPS AI的武装下重新发挥出智能化的威力&#xff0c;今天来聊聊表单的智能化应用会是什么样。 金山智能表单进行数据轻松收集&#xff0c;通过对话或拍照创建表单&#xff0c;回收结果还能自动生成数据报告…

回归预测 | MATLAB实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元多输入单输出回归预测

回归预测 | MATLAB实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现SO-CNN-BiGRU蛇群算法…

浅谈管廊智能监控和报警系统设计探究

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;综合地下管廊为我国城市的发展发挥了积极的推动作用&#xff0c;为了确保综合地下管廊基本功能得以真正的发挥出来&#xff0c;有必要将智能监控系统融入综合地下管廊智能管理系统构建中&#xff0c;以便于实…

windows物理机 上安装centos ,ubuntu,等多个操作系统的要点

一、摘要 一般情况下&#xff0c;我们的笔记本或工作电脑都默认安装windows 分几个区&#xff0c;当下是win7 win8 win 10 win11 等&#xff0c;突然我们有需求需要安装个centos &#xff0c;后面我们应当怎么做&#xff0c;要点是什么&#xff1f;一定要根据网上的贴子一步步来…

【雕爷学编程】MicroPython动手做(37)——驱动LCD与图文显示3

MixPY——让爱(AI)触手可及 MixPY布局 主控芯片&#xff1a;K210&#xff08;64位双核带硬件FPU和卷积加速器的 RISC-V CPU&#xff09; 显示屏&#xff1a;LCD_2.8寸 320*240分辨率&#xff0c;支持电阻触摸 摄像头&#xff1a;OV2640&#xff0c;200W像素 扬声器&#…

python绘制六边形风车,用python画简单的风车

本篇文章给大家谈谈用python画简单的风车&#xff0c;以及python绘制六边形风车&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 风车的动画&#xff0c;过程如下&#xff1a;1&#xff09;绘制风车形状A&#xff0c;2&#xff09;擦除风车形状A&#xff0c;3…

【LinearAlgebra】Chapter 12 - Linear Algebra in Probability Statistics

文章目录 Chapter 12 - Linear Algebra in Probability & StatisticsVariance (around athe mean) 方差&#xff08;接近均值&#xff09;Continuous Probability Distributions 连续概率分布Mean and Variance of p ( x ) p(x) p(x) p ( x ) p(x) p(x) 的均值和方差Norm…

idea-常用插件汇总

idea-常用插件汇总 码云插件 这个插件是码云提供的ps-码云是国内的一款类似github的代码托管工具。 Lombok Lombok是一个通用Java类库&#xff0c;能自动插入编辑器并构建工具&#xff0c;简化Java开发。通过添加注解的方式&#xff0c;不需要为类编写getter或setter等方法…

[MYSQL]查询单位时间消耗量

请求哪位大神给我优化一下mysql语句. 数据库表:(日/月/年 数据表和data_hour结构一样,懒得复制了,不然太长) DROP TABLE IF EXISTS data_source;#数据源 create table IF not EXISTS data_source ( num int unsigned not null auto_increment PRIMARY KEY COMMENT序号 …

关于win11 debian wsl 子系统安装启动docker一直starting,无法启动

首先我先说明&#xff0c;我的步骤都是按照官网步骤来的 通过官网的操作步骤 通过测试命令 sudo docker run hello-world得到下面的命令&#xff0c;我们通过启动命令 sudo service docker start 执行结果如下图 也就是说无法启动&#xff0c;一直显示在启动中 遇到这种情况…

Ubuntu安装git

使用 apt-get install git 安装git 报错&#xff1a; 这个错误信息通常表示您的系统上没有可用的 git 软件包。这可能是因为您的软件源列表中没有包含 git 软件包所在的软件源&#xff0c;或者您的软件源列表已经过期。 解决&#xff1a; 如果您使用的是 Ubuntu 或类似…

Vue2与Vue3响应式原理

Vue2的响应式 Vue3的响应式

(杭电多校)2023“钉耙编程”中国大学生算法设计超级联赛(5)

1001 Typhoon 计算几何 对于每一个避难点,计算其到所有线段的距离,取min即可 AC代码&#xff1a; #include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<deque> #include<cmath> #include<cstdio&…

【云原生】K8S二进制搭建二:部署CNI网络组件

目录 一、K8S提供三大接口1.1容器运行时接口CRI1.2云原生网络接口CNI1.3云原生存储接口CSI 二、Flannel网络插件2.1K8S中Pod网络通信2.2Overlay Network2.3VXLAN2.4Flannel 三、Flannel udp 模式的工作原理3.1ETCD 之 Flannel 提供说明 四、vxlan 模式4.1Flannel vxlan 模式的工…

Packet Tracer - 配置初始路由器设置

Packet Tracer - 配置初始路由器设置 目标 第 1 部分&#xff1a;检验默认路由器配置 第 2 部分&#xff1a;配置并检验初始路由器配置 第 3 部分&#xff1a;保存运行配置文件 拓扑图 背景信息 在本练习中&#xff0c;您将执行基本的路由器配置。您将使用加密密码和明文…

HDFS架构刨析

HDFS架构刨析 概述HDFS架构图整体概述主角色&#xff1a;namenodefsimage内存元数据镜像文件edits log&#xff08;Journal&#xff09;编辑日志 从角色&#xff1a;datanode主角色辅助角色&#xff1a;secondarynamenode 重要特性主从架构分块存储机制副本机制namespace元数据…

京津冀特大暴雨,带给应急通信工作怎样的启示?

上个月&#xff0c;我发卫星基站中移集采那篇文章的时候&#xff0c;就提到&#xff1a;未来即将进入汛期&#xff0c;应急通信装备将发挥重要作用。 果不其然&#xff0c;没多久&#xff0c;超强台风“杜苏芮”形成并登陆福建&#xff0c;造成巨大破坏。 除了在南方地区的肆虐…

Java工程师研学之路【003Java基础语法下】

知识体系 挑战&#xff08;challenge&#xff09; 从终端输入字符串(输入的个数不超过10个)&#xff0c;当输入遇到end字符串时&#xff0c;结束输入并且打印出之前输入的所有字符串。 思路&#xff1a;首先要输出end之前的所有字符串&#xff0c;故可以使用数组把输入的字符…

zookeeper+kafka分布式消息队列集群的部署

目录 一、zookeeper 1.Zookeeper 定义 2.Zookeeper 工作机制 3.Zookeeper 特点 4.Zookeeper 数据结构 5.Zookeeper 应用场景 &#xff08;1&#xff09;统一命名服务 &#xff08;2&#xff09;统一配置管理 &#xff08;3&#xff09;统一集群管理 &#xff08;4&…

零代码爬虫平台SpiderFlow的安装

什么是 Spider Flow &#xff1f; Spider Flow 是一个高度灵活可配置的爬虫平台&#xff0c;用户无需编写代码&#xff0c;以流程图的方式&#xff0c;即可实现爬虫。该工具支持多数据源、自动保存至数据库、任务监控、抓取 JS 动态渲染页面、插件扩展&#xff08;OCR 识别、邮…