【LeetCode】二叉树OJ

目录

一、根据二叉树创建字符串

二、二叉树的层序遍历

三、二叉树的层序遍历 II

四、二叉树的最近公共祖先

五、二叉搜索树与双向链表

六、从前序与中序遍历序列构造二叉树

七、从中序与后序遍历序列构造二叉树


一、根据二叉树创建字符串

606. 根据二叉树创建字符串 - 力扣(LeetCode)

解题思路:本题在递归前序遍历二叉树的同时要注意()的省略,这时就有了三种情况:

  • 如果当前节点没有孩子,我们不需要在节点后面加上任何括号

  • 如果当前节点只有左孩子,我们在递归时,只需要在左孩子的结果外加上一层括号,而不需要给右孩子加上任何括号
  • 如果当前节点只有右孩子,我们在递归时,需要先加上一层空的括号 ‘()’ 表示左孩子为空,再对右孩子进行递归,并在结果外加上一层括号

解题代码: 

class Solution {
public:
    string tree2str(TreeNode* root) {
        if(root==nullptr)
            return "";
        string str=to_string(root->val);
        if(root->left||root->right)//有左孩子或者无左孩子有右孩子
        {
            str+="(";
            str+=tree2str(root->left);
            str+=")";
        }
       if(root->right)//有右孩子
        {
            str+="(";
            str+=tree2str(root->right);
            str+=")";
        }
        return str;
    }
};

二、二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

解题思路:本题为了达到层序遍历,我们需要用到一个队列来保存每一层的节点,将将根节点入队列,每次出队列时都要将其孩子节点入队列:

还要有一个变量来记录每一层节点的个数,当该变量不为0时需要将节点出队列,所出节点都属于同一层的元素,每出一个节点将其减1。当该变量为0时,队列中都是上一层节点的孩子节点,所以都属于同一层节点,这时将变量置为队列中元素个数继续将节点出队列即可,直到队列为空

解题代码:

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> q;
        int levelsize=0;
        if(root)//根节点入队列
        {
            q.push(root);
            levelsize=1;
        }
        vector<vector<int>> vv;
        while(!q.empty())
        {
            vector<int> v;//记录每一层的数据
            while(levelsize--)//将上一层节点出队列的同时,将其孩子节点都入队列
            {
                TreeNode* front=q.front();
                q.pop();
                v.push_back(front->val);
                if(front->left)
                    q.push(front->left);
                if(front->right)
                    q.push(front->right);
            }
            vv.push_back(v);//将遍历完的一层数据存入
            levelsize=q.size();
        }
        return vv;
    }
};

三、二叉树的层序遍历 II

107. 二叉树的层序遍历 II - 力扣(LeetCode)

解题思路:该题是上题的变形题,很多同学容易在这题中想的过多,这一题想要的结果是上题的结果的逆置,所以按上题思路处理,最后将其结果逆置一下即可:

解题代码:

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*> q;
        int levelsize=0;
        if(root)//根节点入队列
        {
            q.push(root);
            levelsize=1;
        }
        vector<vector<int>> vv;
        while(!q.empty())
        {
            vector<int> v;//记录每一层的数据
            while(levelsize--)//将上一层节点出队列的同时,将其孩子节点都入队列
            {
                TreeNode* front=q.front();
                q.pop();
                v.push_back(front->val);
                if(front->left)
                    q.push(front->left);
                if(front->right)
                    q.push(front->right);
            }
            vv.push_back(v);//将遍历完的一层数据存入
            levelsize=q.size();
        }
        reverse(vv.begin(),vv.end());//将结果逆置
        return vv;
    }
};

四、二叉树的最近公共祖先

236. 二叉树的最近公共祖先 - 力扣(LeetCode)

解题思路:对于该题我们需要了解两个节点最近公共祖先的特性,即这两个节点分别在最近公共祖先节点的左右子树;所以我们只要找到满足该条件的节点就找到了最近公共祖先节点

解题代码:

class Solution {
public:
    bool IsIntree(TreeNode* root, TreeNode* x)
    {
        if(root==nullptr)
            return false;
        if(root==x)
            return true;
        return IsIntree(root->left,x)||IsIntree(root->right,x);
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==p||root==q)
            return root;
        bool pInleft=IsIntree(root->left,p);
        bool pInright=!pInleft;
        bool qInleft=IsIntree(root->left,q);
        bool qInright=!qInleft;
        if((pInright&&qInleft)||(pInleft&&qInright))
        {
            return root;
        }
        else if(pInleft&&qInleft)
        {
            return lowestCommonAncestor(root->left,p,q);
        }
        else
        {
            return lowestCommonAncestor(root->right,p,q);
        }
    }
};

但是该方法最坏情况下的时间复杂度为O(N^2),运行时长并不理想:

那下面我们换个思路来优化一下:我们可以记录从根节点到两个要查找节点的路径,从而将其转换为链表相交问题(对于链表的相交问题我们之前在LeetCode和牛客网经典链表题目合集 讲过),对于路径的存储我们可以用到栈来记录,最终找到共同的最近祖先节点:

class Solution {
public:
    bool GetPath(TreeNode* root, TreeNode* x,stack<TreeNode*>& s)
    {
        if(root==nullptr)
            return false;
        s.push(root);
        if(root==x)
            return true;
        if(GetPath(root->left,x,s)||GetPath(root->right,x,s))
            return true;
        s.pop();
        return false;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath,qPath;
        GetPath(root,p,pPath);
        GetPath(root,q,qPath);
        while(pPath.size()!=qPath.size())
        {
            if(pPath.size()>qPath.size())
                pPath.pop();
            else
                qPath.pop();
        }
        while(pPath.top()!=qPath.top())
        {
            pPath.pop();
            qPath.pop();
        }
        return pPath.top();
    }
};

这样子时间复杂度就降到了O(N):

五、二叉搜索树与双向链表

二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

解题思路:在该题中需要利用二叉搜索树中序遍历的结果是升序的,所以我们在中序遍历时修改所在节点与前驱节点的指针指向即可;用cur指针表示当前节点,用prev指针表示中序遍历时当前节点的上一个遍历节点,使cur指针的左孩子:

下面是一个二叉搜索树中序遍历时,修改当前节点和前驱节点的指针指向的演示:

解题代码:

class Solution {
public:
	void InOrder(TreeNode* cur,TreeNode*& prev)
	{
		if(cur==nullptr)
			return;
		InOrder(cur->left,prev);
		cur->left=prev;
		if(prev)
			prev->right=cur;//让前驱节点指针的右孩子指向当前节点
		prev=cur;//更新前驱节点指针位置
		InOrder(cur->right,prev);
	}
    TreeNode* Convert(TreeNode* pRootOfTree) 
	{
		if(pRootOfTree==nullptr)
			return nullptr;
		TreeNode* prev=nullptr;
        InOrder(pRootOfTree,prev);//中序遍历的同时修改二叉树节点指针的指向
		TreeNode* head=pRootOfTree;//pRootOfTree并非链表的头节点,需要找到头节点后返回
		while(head->left)
		{
			head=head->left;
		}
		return head;
    }
};

六、从前序与中序遍历序列构造二叉树

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

解题思路:

对于任意一颗树而言,前序遍历的形式总是

[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]

即根节点总是前序遍历中的第一个节点。而中序遍历的形式总是

[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位

这样以来,我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置

解题代码:

class Solution {
public:

    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,int begini,int endi,int& n) 
    {
        if(begini>endi)
            return nullptr;
        TreeNode* newnode=new TreeNode(preorder[n]);//先利用先序遍历确定根节点
        int rooti=begini;
        while(rooti<=endi)//找到确定的根节点在中序遍历中的位置
        {
            if(inorder[rooti]==preorder[n])
                break;
            else
                ++rooti;
        }
        ++n;
        newnode->left=_buildTree(preorder,inorder,begini,rooti-1,n);//将剩下的左区间的节点连接到左子树上
        newnode->right=_buildTree(preorder,inorder,rooti+1,endi,n);//将剩下的右区间的节点连接到右子树上
        return newnode;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n=0;
        return _buildTree(preorder,inorder,0,preorder.size()-1,n);
    }
};

七、从中序与后序遍历序列构造二叉树

106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

解题思路:该题和上一题是同类型题,我们使用后序遍历的逆序来确定根节点,再在中序遍历中找到其对应的节点,再递归地对构造出右子树和左子树(后序遍历的逆序是相当于先遍历右子树的前序遍历,所以我们先要构建右子树再构建左子树)

解题代码:

class Solution {
public:
    TreeNode* _buildTree(vector<int>& inorder,vector<int>& postorder,int begini,int endi,int& n) 
    {
        if(begini>endi)
            return nullptr;
        TreeNode* newnode=new TreeNode(postorder[n]);//先利用后序遍历确定根节点
        int rooti=begini;
        while(rooti<=endi)//找到确定的根节点在中序遍历中的位置
        {
            if(inorder[rooti]==postorder[n])
                break;
            else
                ++rooti;
        }
        --n;
        newnode->right=_buildTree(inorder,postorder,rooti+1,endi,n);//将剩下的右区间的节点连接到右子树上
        newnode->left=_buildTree(inorder,postorder,begini,rooti-1,n);//将剩下的左区间的节点连接到左子树上
        return newnode;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int n=inorder.size()-1;
        return _buildTree(inorder,postorder,0,inorder.size()-1,n);
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024全网最新最全的Pytest接口自动化测试框架教程

pytest编写的规则&#xff1a; 1、测试文件以test_开头&#xff08;以_test结尾也可以&#xff09; 2、测试类以Test开头&#xff0c;并且不能带有__init__方法 3、测试函数以test_开头 4、断言必须使用assert pytest.main([-s,-v]) &#xff1a;用来执行测试用例 -s 打印prin…

计算机毕业设计 基于SpringBoot的健身房管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解目录

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

linux系统环境下mysql安装和基本命令学习

此篇文章为蓝桥云课--MySQL的学习记录 块引用部分为自己的实验部分&#xff0c;其余部分是课程自带的知识&#xff0c;链接如下&#xff1a; MySQL 基础课程_MySQL - 蓝桥云课 本课程为 SQL 基本语法及 MySQL 基本操作的实验&#xff0c;理论内容较少&#xff0c;动手实践多&am…

033、微调

之——高级炼丹术 目录 之——高级炼丹术 杂谈 正文 1.标注数据集是很贵的 2.微调的思想 3.尝试 小结 杂谈 微调&#xff08;Fine-tuning&#xff09;是深度学习中的一种常见策略&#xff0c;它通常用于预训练模型在特定任务上的性能提升。微调的过程涉及在一个已经在大…

7.22 SpringBoot项目实战【收藏 和 取消收藏】

文章目录 前言一、编写控制器二、编写服务层三、Postman测试最后前言 本系统还支持 收藏图书,就是对心仪的书加一下收藏,大家都懂,这是一个很常见的功能。 那么我们来看看怎么来做,先分析一下:【一个人】对【一本书】只需【收藏一次】,但可以【收藏N本】不同的书,收藏…

2024年csdn最新最全面的fiddler教程【1】

Fiddler简介 Fiddler是比较好用的web代理调试工具之一&#xff0c;它能记录并检查所有客户端与服务端的HTTP/HTTPS请求&#xff0c;能够设置断点&#xff0c;篡改及伪造Request/Response的数据&#xff0c;修改hosts&#xff0c;限制网速&#xff0c;http请求性能统计&#xff…

基于springboot实现校园在线拍卖系统项目【项目源码】计算机毕业设计

基于springboot实现校园在线拍卖系统演示 Javar技术 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&…

高效管理文件:如何通过文件数量归类提高工作效率

在日常生活和工作中&#xff0c;需要处理大量的文件和资料。然而&#xff0c;如果这些文件没有得到妥善的管理&#xff0c;就会使得我们花费大量的时间和精力去寻找和整理它们。对于大量文件&#xff0c;按照数量归类可以使得文件管理更加有序和规范。根据文件的数量建立相应的…

Go 语言中切片的使用和理解

切片与数组类似&#xff0c;但更强大和灵活。与数组一样&#xff0c;切片也用于在单个变量中存储相同类型的多个值。然而&#xff0c;与数组不同的是&#xff0c;切片的长度可以根据需要增长和缩小。在 Go 中&#xff0c;有几种创建切片的方法&#xff1a; 使用[]datatype{val…

深信服AC流量管理技术

拓扑图 一.保证通道针对修仙部&#xff0c;访问网站&#xff0c;邮件&#xff0c;DNS&#xff0c;IM&#xff0c;办工 OA&#xff0c;微博论坛网上银行等常见应用保证带宽最低 50%&#xff0c;最高 100% 1. 先新建线路带宽 2.新增流量管理通道&#xff08;保证关键应用&#x…

Selenium UI 自动化

一、Selenium 自动化 1、什么是Selenium&#xff1f; Selenium是web应用中基于UI的自动化测试框架。 2、Selenium的特点&#xff1f; 支持多平台、多浏览器、多语言。 3、自动化工作原理&#xff1f; 通过上图&#xff0c;我们可以注意到3个角色&#xff0c;下面具体讲解一…

16. @PostConstruct注解和开关原理(验证码开关、IP开关)

1►PostConstruct注解 PostConstruct是java自带的注解&#xff0c;会在java项目启动的时候先执行下面的方法 2►开关原理&#xff08;验证码开关&#xff09; 我们的项目具有验证码功能&#xff0c;旧版不支持关闭&#xff0c;新版已经支持关闭了。 我们打开页面“参数管…

腾讯云轻量数据库性能如何?轻量数据库租用配置价格表

腾讯云轻量数据库测评&#xff0c;轻量数据库100%兼容MySQL 5.7和8.0&#xff0c;腾讯云提供1C1G20GB、1C1G40GB、1C2G80GB、2C4G120GB、2C8G240GB五种规格轻量数据库&#xff0c;腾讯云百科txybk.com分享腾讯云轻量数据库测评、轻量数据库详细介绍、特性、配置价格和常见问题解…

运行ps显示msvcp140.dll丢失怎么恢复?msvcp140.dll快速解决的4个不同方法

msvcp140.dll无法继续执行代码的主要原因有以下几点 系统缺失&#xff1a;msvcp140.dll是Visual Studio 2015编译的程序默认的库文件&#xff0c;如果系统中没有这个库文件&#xff0c;那么在运行相关程序时就会出现找不到msvcp140.dll的错误提示。 文件损坏&#xff1a;如果…

C语言入门笔记—static、extern、define、指针、结构体

一、static static修饰局部变量的时候&#xff0c;局部变量出了作用域&#xff0c;不销毁。本质上&#xff0c;static修饰局部变量的时候&#xff0c;改变了变量的存储位置。详见下图&#xff0c;当a不被static修饰和被static修饰的时候。 C/C static关键字详解&#xff…

TensorRT量化实战课YOLOv7量化:YOLOv7-QAT量化

目录 前言1. YOLOv7-QAT流程2. QAT训练流程 前言 手写 AI 推出的全新 TensorRT 模型量化实战课程&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考。 该实战课程主要基于手写 AI 的 Latte 老师所出的 TensorRT下的模型量化&#xff0c;在其课程的基础上&#xff…

SPASS-聚类和判别分析

聚类与判别分析概述 基本概念 聚类分析 聚类分析的基本思想是找出一些能够度量样本或指标之间相似程度的统计量&#xff0c;以这些统计量为划分类型的依据&#xff0c;把一些相似程度较大的样本&#xff08;或指标&#xff09;聚合为一类&#xff0c;把另外一些彼此之间相似程…

VMware——WindowServer2012R2安装jdk1.8及环境变量配置

一、安装 双击【jdk-8u161-windows-x64.exe】程序包&#xff0c;弹出窗口点击【下一步】&#xff0c;如下图&#xff1a; 指定安装目录为【Java\jdk1.8.0_161】&#xff0c;磁盘目录自定义&#xff0c;如下图&#xff1a; 点击【下一步】一直到有个【更改】按钮&#xff0c;可…

Network(五)数值介绍与子网划分

一 数值 1 数值介绍 &#xff08;1&#xff09;带宽 在一定时间内通过某一网络连接的信息量 基本单位&#xff1a;比特每秒 (bit/s) 在计算机软件方面用字节每秒为单位 &#xff08;2&#xff09;存储量 计算机存储量可以用位和字节计量 &#xff08;3&#xff09;常用…

C语言——2.安装并使用VS

文章目录 1.编译器是什么2.编译器的选择2.1.VS2019/2022 的初步了解2.2.为什么不选择其他编译器呢&#xff1f; 3.编译器的安装过程&#xff08;保姆级别教学&#xff09;3.1.检查电脑版本3.2.下载安装包3.3.选择安装选项3.4.重启电脑3.5.创建账户登录3.6.颜色配置3.7.VS&#…