进阶了解C++(6)——二叉树OJ题

news2024/9/21 0:52:05

Leetcode.606.根据二叉树创建字符串:

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

难度不大,根据题目的描述,首先对二叉树进行一次前序遍历,即:

class Solution {
public:
    string tree2str(TreeNode* root) {

        if(root == nullptr)
        {
            return "";
        }

        string ret = to_string(root->val);

        ret += tree2str(root->left);
        ret += tree2str(root->right);
        return ret;
    }
};

输出结果如下:

       从结果看到,元素输出的顺序正确,但是输出的格式不正确。对于题目要求的格式输出,不难发现,当一个结点的左子树的结点为空时,括号不输出。当一个结点的左子树的结点为空,但是右子树的结点不为空时,正常打印左,右结点的括号。只有当右结点存在时,才打印右结点的括号。

      对于或逻辑运算符'\left | \right |'的运算逻辑如下:a\left | \right |b\left | \right |c\left | \right |d假如a为真,则不在对下面的情况进行判断,假如a为假,则会挨个事件进行判断,如果a,b,c,d全为假才为假。在上面说到,对于一个结点的左子树根结点括号是否需要打印要分为两种情况:

      1.结点的左子树结点为空,但是结点的右子树结点不为空,此时正常对结点的左子树结点的括号进行打印。

      2.结点的左子树结点为空,同时右子树结点为空,此时不打印任何括号。

      因此,对于上面两种结点情况的判定,可以使用\left | \right |逻辑完成。首先判断root->left是否为空,如果不为空,则正常打印左结点的括号。 如果为空,则去判断root->right,如果不为空,则正常打印左结点的括号,如果为空,则不打印左结点的括号。

      对于右结点情况的判定,如果右结点为空,则不打印括号,如果不为空,则正常打印,因此,对应代码如下:
 

class Solution {
public:
    string tree2str(TreeNode* root) {

        if(root == nullptr)
        {
            return "";
        }

        string ret = to_string(root->val);
        if(root->left || root->right)
        {
            ret+='(';
        ret += tree2str(root->left);
        ret+=')';
        }
        if(root->right)
        {
            ret+='(';
        ret += tree2str(root->right);
        ret+=')';
        }
        return ret;
    }
};

 运行结果如下:

Leetcode.102 二叉树的层序遍历:

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

对于本题,可以利用队列来实现,具体思路如下:

    首先利用栈,将二叉树根结点所对应的元素压入队列,即:

      由于层序遍历的特点,因此在下一步,需要将3的左右子树的结点9,20进入队列。不过此时会面临一个问题,如何判断下一层全部进入队列?对于图中的树,第二层只有两个结点,姑且可以人为创建两次入队列的操作,但是对于下面层的结点,例如结点的数量为x个,不好判断入队列的次数。但是,对于这x个结点,因为其父结点的原因。是绝对可以在x/2次数内入完队列的。所以,对于入队列的次数,可以分成x/2次数次完成,即通过其父结点来完成。

    所以,额外定义一个变量levelsize来记录本层结点,即下一层结点的父结点的数量,例如对于上图中的队列,levelsize=1

   随后,利用循环,循环levlesize次,保证子结点都能入到队列中。由于题目要求的输出方式是类似于二维数组的方式,所以需要额外创建一个vector<int>类型的变量v用于向二维数组中不断插入层序遍历到的结点。

   对于上述步骤,仅仅用文字描写不够清晰,下面将利用图片进行演示:
   首先,层序遍历每一层的结点的结果在输出时是独立的,因此,在进入循环后,首先创建一个指针frontqueue中的结点的地址进行保存,并且将这个结点出队列。即:

然后,将这个结点的数值插入到v中,即:

随后,让front的左右结点依次入队列,即:

随后,令v插入到二维数组vv中,并且由于此时队列中存储了两个结点,所以改变levelsize,即:

至此,一次循环运行完毕。由于第一层的levelsize==1,因此上述步骤只会进行一次。为了便于理解,再给出第二次循环的相关图片解释。

此时队列中存储了9,20两个结点的地址。首先利用front结点保存队列中第一个结点的地址。即:

然后让队列头部元素出队列,并且将这个元素插入到v中,即:

随后,让front的左右结点依次进队列,即:

由于levelsize==2,因此,上述步骤还会进行一次,第二次具体如下:
利用front保存队列的头部元素,然后出队列的头部元素,再令v保存front->val即:

然后在令front左右结点的地址进入队列,即:

最后,令vv保存v,再改变levelsize,即:

对应代码如下:
 

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {

        vector<vector<int>> vv;
        //用于进出每一次的结点
        queue<TreeNode*> q;
        //记录每层结点数量
        int levelsize = 0;

        if(root)
        {
            q.push(root);
            levelsize = 1;
        }

        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);
            }

            levelsize = q.size();
            vv.push_back(v);
        }
        return vv;

    }
};

在上面的代码中,需要注意,由于v保存的是某一层的结点,与上层无关,因此,v定义在第一层while循环中,可以保证,每次循环执行完毕后,v都会进行一次自动的更新。

运行结果如下:

Leetcode.236 二叉树的最近公共祖先:

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

本题可以说是本文中难度最大题目。对于题目中公共祖先的定义,可以分为下面的类型:
例如对于下面给出的二叉树中,结点7,0的最近公共祖先为根结点,即存储值为3的结点:

对于结点6,4的最近公共祖先为存储值为5的结点,即:

对于结点2,4的最近公共祖先是存储值为2的结点,即:

通过对于上面不同结点所对应的公共结点,可以得到寻找公共结点的规律:

1.如果给定的两个结点p,q分别在树的左子树和右子树,则根结点root就是最近公共结点。(对应第一个图所对应的情况)。

2.如果给定的两个结点p,q同时在树的左子树或者同时在树的右子树,(对应第二个图所对应的情况),此时,可以去判断p,q是否在根结点的子结点的左右,如果在,则根结点的子结点就是p,q的最近公共祖先,即:

3.如果p,q中的一个是另一个的子结点,例如假设pq的子结点,则q就是最近公共结点。

因此,在查找p,q的最近公共祖先之前,需要先确定p,q两个结点在树中的位置。二者的位置可以分为下面四种情况: p结点在左子树,p结点在右子树,q结点在左子树,q结点在右子树。为了方便表示,用pInleft,pInright,qInleft,qInright分别表示上面的四种情况。对于上面结点位置的查询,可以使用递归来实现,具体代码如下:

bool IsInTree(TreeNode* root, TreeNode* x)
    {
        if(root == nullptr)
        {
            return false;
        }

        return root == x || IsInTree(root->left,x) || IsInTree(root->right,x);
    }

同时,如果题目给定的树为空树,则不需要进行判断,如果给定的p,q其中之一就是根结点,则直接返回根结点即可。

在利用上面的代码确定了p,q结点在树中的位置后,需要分下面的情况进行判定:
入如果pInleft,qInright同时为真或者pInright,qInleft同时为真,则说明p,q结点分别分布在左子树或者右子树。因此对于这种情况,直接返回根结点即可。

如果pInleft,qInleft同时为真或者pInright,qInright同时为真,则说明p,q同时在左子树或者右子树。因此直接对其根结点左子树,或者右子树进行重复判断即可。对应代码如下:
 

class Solution {
public:
    bool IsInTree(TreeNode* root, TreeNode* x)
    {
        if(root == nullptr)
        {
            return false;
        }

        return root == x || IsInTree(root->left,x) || IsInTree(root->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,qInleft,qInright;

        pInleft = IsInTree(root->left,p);
        pInright = ! pInleft;

        qInleft = IsInTree(root->left,q);
        qInright = ! qInleft;
        
        if((pInleft && qInright) || (pInright && qInleft))
        {
            return root;
        }
        else if((pInleft && qInleft) )
        {
            return lowestCommonAncestor(root->left,p,q);
        }
        else if((qInright && pInright))
        {
            return lowestCommonAncestor(root->right,p,q);
        }

       assert(false);
        return nullptr;
    }
};

运行结果如下:

但是这种方法时间复杂度太大。因此,可以采用下面的方法来优化时间复杂度:

例如对于上面的树,如果将查找两个结点的路径记录,则记录的结点如下:

则不难发现,所谓的最近公共祖先,就是二者路径上最近的相同结点。而重点,就是如何去记录这个路径。文章给出一种方法,具体如下:
首先将寻找路径的函数命名为GetPath,首先检测当前结点是否为空,如果为空,则直接返回false,如果不为空,首先创建一个栈,这里将这个栈命名为s,让结点入栈。在入栈后,检测当前结点是否是需要寻找的结点,如果是则返回true。如果不是,则分别递归结点的左右子树。如果在左右子树中没有找到,则出栈一次。为了方便理解,下面用图来演示这一过程:

首先,此时的结点3不为空,因此直接入栈,即:

由于3并不是需要找的结点,因此先去结点的左子树中进行寻找,即5。与上方相同的逻辑,首先让5入栈,即:

由于5也不是需要查找的结点,因此按照递归,再去左子树进行查找。

让结点6入栈,即:

由于6也不是需要找的结点,因此继续往左子树递归。由于6结点的左,右子树结点都为空,因此判定为左,右子树都找不到结点,所以将6弹出栈,即:

此时递归回到上一层,由于5结点的左子树找不到需要的结点,因此去其右子树进行查找,即将2入栈;


同时,由于2也不是需要找的结点,因此去其左子树进行查找,即将7入栈。由于此时的结点为需要查找的结点,因此返回true。此时栈中内容如下;

此时,便获取了查找结点的路径。

对于另一个需要查找的结点,由于原理相同,因此不再过多叙述。

在得到了两个结点的路径后,首相让长的路径逐渐pop,直到两个栈的size相同即可。随后挨个比较栈中元素。如果相同则返回即可。对应代码如下:
 

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))
        {
            return true;
        }

        if(GetPath(root->right,x,s))
        {
            return true;
        }

        s.pop();
        return false;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {

        stack<TreeNode*> sp;
        stack<TreeNode*> sq;

        GetPath(root,p,sp);
        GetPath(root,q,sq);

        while(sp.size() != sq.size())
        {
            if(sp.size() > sq.size())
            {
                sp.pop();
            }
            else
            {
                sq.pop();
            }
        }

        while(sp.top() != sq.top())
        {
            sp.pop();
            sq.pop();
        }

        return sp.top();
        
    }
};

运行结果如下:

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

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

相关文章

【管理咨询宝藏59】某大型汽车物流战略咨询报告

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏59】某大型汽车物流战略咨询报告 【格式】PDF 【关键词】HR调研、商业分析、管理咨询 【核心观点】 - 重新评估和调整商业模式&#xff0c;开拓…

代码随想录——移除元素(Leetcode27)

题目链接 暴力&#xff1a;&#xff08;没有改变元素相对位置&#xff09; class Solution {public int removeElement(int[] nums, int val) {int len nums.length;for(int i 0; i < len; i){if(nums[i] val){for(int j i 1; j < len; j){nums[j-1] nums[j];}i…

ESCTF-密码赛题WP

*小学生的爱情* Base64解码获得flag *中学生的爱情* 社会主义核心价值观在线解码得到flag http://www.atoolbox.net/Tool.php?Id850 *高中生的爱情* U2FsdG开头为rabbit密码,又提示你密钥为love。本地toolfx密码工具箱解密。不知道为什么在线解密不行。 *大学生的爱情* …

LLM - 大语言模型的指令微调(Instruction Tuning) 概述

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/137009993 大语言模型的指令微调(Instruction Tuning)是一种优化技术&#xff0c;通过在特定的数据集上进一步训练大型语言模型(LLMs)&a…

STM32 PWM通过RC低通滤波转双极性SPWM测试

STM32 PWM通过RC低通滤波转双极性SPWM测试 &#x1f4cd;参考内容《利用是stm32cubemx实现双极性spwm调制 基于stm32f407vet6》&#x1f4fa;相关视频链接&#xff1a;https://www.bilibili.com/video/BV16S4y147hB/?spm_id_from333.788 双极性SPWM调制讲解以及基于stm32的代码…

线程的状态:操作系统层面和JVM层面

在操作系统层面&#xff0c;线程有五种状态 初始状态&#xff1a;线程被创建&#xff0c;操作系统为其分配资源。 可运行状态(就绪状态)&#xff1a;线程被创建完成&#xff0c;进入就绪队列&#xff0c;参与CPU执行权的争夺。或因为一些原因&#xff0c;从阻塞状态唤醒的线程…

Deno 1.42:使用 JSR 更好地进行依赖管理

3 月 28 日&#xff0c;Deno 宣布 1.42 版本正式推出。Deno 的愿景是简化编程&#xff0c;其中一个重要方面就是管理依赖关系。虽然 npm 已发展成为最成功的开源注册表&#xff0c;但使用和发布模块却变得越来越复杂。 基于 npm 的成功&#xff0c;JSR 提供​​了一个现代化的…

如何使用PMKIDCracker对包含PMKID值的WPA2密码执行安全测试

关于PMKIDCracker PMKIDCracker是一款针对无线网络WPA2密码的安全审计与破解测试工具&#xff0c;该工具可以在不需要客户端或去身份验证的情况下对包含了PMKID值的WPA2无线密码执行安全审计与破解测试。 PMKIDCracker基于纯Python 3开发&#xff0c;旨在帮助广大安全研究人员…

MongoDB副本集环境搭建(以单机Windows为例)

前言 近期有搭建MongoDB副本集的需求,简单记录一下搭建过程(以本地Windows环境为例)。 一、副本集选型 1 Primary节点、1 Secondary 节点、1 Arbiter节点模式副本集环境搭建。 二、搭建过程 1. 安装MongoDB服务 下载地址:https://www.mongodb.com,如下图所示: 选择…

sonar+gitlab提交阻断 增量扫描

通过本文&#xff0c;您将可以学习到 sonarqube、git\gitlab、shell、sonar-scanner、sonarlint 一、前言 sonarqube 是一款开源的静态代码扫描工具。 实际生产应用中&#xff0c;sonarqube 如何落地&#xff0c;需要考虑以下四个维度&#xff1a; 1、规则的来源 现在规则的…

HTTP,Servlet

HTTP 概念&#xff1a;HyperTextTransferProtocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则 HTTP协议特点&#xff1a; 1.基于TCP协议&#xff1a;面向连接&#xff0c;安全 2.基于请求-响应模型的&#xff1a;一次请求对应一次响应 …

【数据挖掘】实验5:数据预处理(2)

验5&#xff1a;数据预处理&#xff08;2&#xff09; 一&#xff1a;实验目的与要求 1&#xff1a;熟悉和掌握数据预处理&#xff0c;学习数据清洗、数据集成、数据变换、数据规约、R语言中主要数据预处理函数。 二&#xff1a;实验知识点总结 1&#xff1a;数据集成是将多个…

基于jsp+mysql+Spring+hibernate+的SSH在线学习交流论坛平台

基于jspmysqlSpringhibernate的SSH在线学习交流论坛平台 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末…

如何在Windows 10中打开屏幕键盘?这里有详细步骤

本文解释了在Windows 10中打开或关闭屏幕键盘的不同方法&#xff0c;还解释了如何将屏幕键盘固定到开始菜单。 使用屏幕键盘的快捷键 如果你喜欢快捷方式&#xff0c;你会喜欢这个&#xff1a;按物理键盘上的WinCTRLO。这将立即显示屏幕键盘&#xff0c;而无需通过轻松使用。…

Pytorch复现 Transformer cssdn

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;TensorFlow入门实战&#xff5c;第3周&#xff1a;天气识别&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 一、多头注意力机制 import torch import…

ElementUI表格table组件实现单选及禁用默认选中效果

在使用ElementUI&#xff0c;需要ElementUI表格table组件实现单选及禁用默认选中效果, 先看下效果图&#xff1a; 代码如下&#xff1a; <template><el-tableref"multipleTable":data"tableData"tooltip-effect"dark"style"widt…

Linux根据时间删除文件或目录

《liunx根据时间删除文件》和 《Linux 根据时间删除文件或者目录》已经讲述了根据时间删除文件或目录的方法。 下面我做一些补充&#xff0c;讲述一个具体例子。以删除/home目录下的文件为例。 首先通过命令&#xff1a; ls -l --time-style"%Y-%m-%d %H:%M:%S"…

免费分享一套SpringBoot+Vue个人健康管理系统,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue个人健康管理系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue个人健康管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue个人健康管理系统 Java毕业设计项目来…

《亮数据:爬虫数据采集行业痛点的利器》

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

[寿司力扣DP对于应用]712. 两个字符串的最小ASCII删除和【详细图解】

712. 两个字符串的最小 ASCII 删除和 LeetCode 原题链接 题目描述 给定两个字符串 s1 和 s2&#xff0c;返回 使两个字符串相等所需删除字符的ASCII 值的最小和 。 示例 1: 输入: s1 “sea”, s2 “eat” 输出: 231 解释: 在 “sea” 中删除 “s” 并将 “s” 的值(115)…