热门二叉树面试题

news2024/10/5 14:32:05

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

给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。

空节点使用一对空括号对 "()" 表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

示例 1:

输入:root = [1,2,3,4]
输出:"1(2(4))(3)"
解释:初步转化后得到 "1(2(4)())(3()())" ,但省略所有不必要的空括号对后,字符串应该是"1(2(4))(3)" 。

示例 2:

输入:root = [1,2,3,null,4]
输出:"1(2()(4))(3)"
解释:和第一个示例类似,但是无法省略第一个空括号对,否则会破坏输入与输出一一映射的关系。

分析:示例一当中输入1,2,3,4,然后输出1,2,4,3。那么已经很容易看出来是一个二叉树前序遍历了,二叉树的前序遍历顺序为中 左 右,解决了顺序问题,接下来就是括号的问题了。

一共有四种情况

1:只有左儿子

2:只有右儿子

3:没有左右儿子

4:有左右儿子

然后化简之后可以得到1(2(4))(3)

class Solution {
public:

    void Pre(TreeNode* t,string& str)
    {
        if(t==nullptr) return;//为空直接返回,包括了没有左右儿子的情况,直接不打印

        str+=to_string(t->val);
        if(t->left)//左子树
        {
            str+="(";
            Pre(t->left,str);
            str+=")";
        }
        
        if(t->right)//右子树
        {
            str+="(";
            Pre(t->right,str);
            str+=")";
        }

    }

    string tree2str(TreeNode* root) {
        string res="";
        Pre(root,res);
        return res;
    }
};

但是和结果好像不一样,少了一个括号,我们来模拟一下。

当递归到节点2的时候,2->left为空,就直接返回了,返回了之后就会导致这里没有打印任何东西,所以导致结果出错,因此第一个if条件可以改为if(t->left||t->right)

class Solution {
public:

    void Pre(TreeNode* t,string& str)
    {
        if(t==nullptr) return;//为空直接返回,包括了没有左右儿子的情况,直接不打印

        str+=to_string(t->val);
        if(t->left||t->right)//左子树/左子树和右子树都存在
        {
            str+="(";
            Pre(t->left,str);
            str+=")";
        }
        
        if(t->right)//右子树
        {
            str+="(";
            Pre(t->right,str);
            str+=")";
        }

    }

    string tree2str(TreeNode* root) {
        string res="";
        Pre(root,res);
        return res;
    }
};

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

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

层序遍历,BFS,老套路题了,用一个队列来模拟遍历的过程。

比如一开始3入队,拿到节点3,3出队,然后把3还有3的左右子节点加入答案,把9和20加入队列,然后拿到节点9,出队,9没有左右子节点,所以继续拿到节点20,20再出队,把20和20的左右子节点都加入答案即可。

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>>res;
        if(!root) return res;
        
        queue<TreeNode*>q;
        q.push(root);
        while(!q.empty())
        {
            vector<int>tmp;
            int sz=q.size();//记录当前层数有多少节点,就要循环取多少次,一定要提前记录,不然q.size()会改变。
            for(int i=0;i<sz;i++)
            {
                auto node=q.front();//取队头
                tmp.push_back(node->val);//入暂存的答案数组
                q.pop();//出队
                if(node->left)
                {
                    q.push(node->left);
                }
                if(node->right)
                {
                    q.push(node->right);
                }
            }
            res.push_back(tmp);
        }
        return res;
    }
};

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

给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

这题没什么说的,在上一题的基础上对结果进行逆置就好了。

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*>q;
        if(root!=nullptr)
        {
            q.push(root);
        }
        vector<vector<int>>res;
        while(!q.empty())
        {
            int size=q.size();
            vector<int> vec;
            for(int i=0;i<size;i++)
            {
                auto node=q.front();
                q.pop();
                vec.push_back(node->val);
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            res.push_back(vec);
        } 
        reverse(res.begin(),res.end());
        return res;
    }
};

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

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

这道题一共有三种情况。

1:都在左子树

2:都在右子树

3:一个在左,一个在右

首先看最好判断的一个在左一个在右的情况这种情况的最近公共祖先就是根节点。

然后是在左子树的:我们只需要在左子树搜索第一个找到p或者q的那个节点就是最近公共祖先。

右子树和左子树同理。

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==nullptr||root==p||root==q) return root;
        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);
        if(left==nullptr) return right;
        else if(right==nullptr) return left;
        return root;
    }
};

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

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

前序遍历顺序:中 左 右

中序遍历顺序:左 中 右

可以看出来,前序遍历的第一个节点就是根节点,找到根节点就可以利用中序遍历来确定左右子树的个数

比如下图,前序遍历的第一个节点是3,那么根节点就是3,再去中序遍历找3,以3为界限划分左右子树,9就是左边的,20,15,7算是右边的。

class Solution {
public:

    unordered_map<int,int>pos;

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        for(int i=0;i<inorder.size();i++)
        {
            pos[inorder[i]]=i;
        }
        return build(preorder,inorder,0,preorder.size()-1,0,inorder.size()-1);
    }

    TreeNode* build(vector<int>& preorder,vector<int>& inorder,int pl,int pr,int il,int ir)
    {
        if(pl>pr) return nullptr;
        TreeNode* root=new TreeNode(preorder[pl]);//前序遍历的第一个元素是根节点
        int pos1=pos[root->val];
        root->left=build(preorder,inorder,pl+1,pl+1+pos1-1-il,il,pos1-1);
        root->right=build(preorder,inorder,pl+1+pos1-il,pr,pos1+1,ir);
        return root;
    }
};

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

给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]

思路和上题几乎一样,分析如上图

class Solution {
public:

    unordered_map<int,int>pos;
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        for(int i=0;i<inorder.size();i++)
        {
            pos[inorder[i]]=i;//记录中序遍历的每个点的位置
        }
        return build(inorder,postorder,0,inorder.size()-1,0,postorder.size()-1);
    }

    TreeNode* build(vector<int>& inorder,vector<int>&postorder,int il,int ir,int pl,int pr)
    {
        if(pl>pr)return nullptr;
        TreeNode* root=new TreeNode(postorder[pr]);
        int k=pos[root->val];
        root->left=build(inorder,postorder,il,k-1,pl,pl+(k-1-il));
        root->right=build(inorder,postorder,k+1,ir,pl+k-il,pr-1);
        return root;
    }
};

144. 二叉树的前序遍历 - 力扣(LeetCode)

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

输入:root = [1,null,2,3]
输出:[1,2,3]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

示例 4:

输入:root = [1,2]
输出:[1,2]

示例 5:

输入:root = [1,null,2]
输出:[1,2]

前序遍历递归版本很简单:

class Solution {
public:
    vector<int>res;
    vector<int> preorderTraversal(TreeNode* root) {
        pre(root);
        return res;
    }

    void pre(TreeNode* root)
    {
        if(root==nullptr)return;
        res.push_back(root->val);
        pre(root->left);
        pre(root->right);
    }
};

非递归版本:

前序遍历为:中 左 右,所以先一直往节点的左节点走,然后再走右边,边走边将当前节点的值加入答案当中即可。

这里可以用一个栈来模拟一下过程,假如是这样一棵树

一直循环走左节点1 2 4 6,并且将其加入栈和答案,如果没有右节点就会一直pop栈内的元素,一直到节点2,2存在右节点,就将5加入栈和答案,5没有右节点,就会继续出栈,一直出到1,节点1存在右节点,就讲3又加入栈和答案,最后pop,然后返回答案即可。

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        if (root == nullptr) {
            return res;
        }

        stack<TreeNode*> st;
        TreeNode* node = root;
        while(node!=nullptr||!st.empty())
        {
            while(node!=nullptr)
            {
                //遍历左子树
                st.push(node);
                res.push_back(node->val);
                node=node->left;
            }
            //遍历了左子树,就要取节点然后遍历右子树了
            node=st.top();
            st.pop();
            node=node->right;
        }
        return res;
    }
};

94. 二叉树的中序遍历 - 力扣(LeetCode)

给定一个二叉树的根节点 root ,返回 它的 中序 遍历

示例 1:

输入:root = [1,null,2,3]
输出:[1,3,2]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

递归版本:

class Solution {
public:
    vector<int>res;
    vector<int> inorderTraversal(TreeNode* root) {
        inorder(root);
        return res;
    }

    void inorder(TreeNode* root)
    {
        if(root==nullptr) return;
        inorder(root->left);
        res.push_back(root->val);
        inorder(root->right);
    }
    
};

非递归版本:

同样对于如图的一棵树

中序遍历:左 中 右

6 4 2 5 1 3是中序遍历的答案。

中序遍历和前序遍历的非递归方式不同点在于,不能在路循环走左子树的时候一并把结点的值加入答案,只能加入栈当中。等到走完了左子树才能够将当前节点加入到答案当中,然后继续取栈中的节点,然后pop栈中的节点,再去走右节点。

class Solution {
public:
    vector<int>res;
    stack<TreeNode*> st;
    vector<int> inorderTraversal(TreeNode* root) {
        if(root==nullptr) return res;

        TreeNode* node=root;
        while(node!=nullptr||!st.empty())
        {
            while(node!=nullptr)
            {
                st.push(node);
                node=node->left;
            }
            node=st.top();
            st.pop();
            res.push_back(node->val);
            node=node->right;
        }
        return res;
    }
};

145. 二叉树的后序遍历 - 力扣(LeetCode)

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历

示例 1:

输入:root = [1,null,2,3]
输出:[3,2,1]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

直接给非递归版本吧

这里最需要注意的一点就是避免重复将一个节点加入答案当中最后造成死循环。

和前序、中序遍历一样,先走完左子树,然后判断右子树,假如我只判断右子树是否为空,在走到节点7回到节点2的时候,这时候要去走2->right,很明显,2->right存在,所以4会被加入答案和栈当中,这时候将节点node置为nullptr,就能避开继续走左子树,直接取栈顶的节点4,4没有右子树了,所以节点4又会被加入到栈当中,就会造成死循环。

在这里可以利用一个pre节点来保存上一次加入栈当中的节点,然后判断是否和pre重复。

class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> res;
        if (root == nullptr) {
            return res;
        }

        stack<TreeNode *> st;//stack
        TreeNode* node=root;//当前节点
        TreeNode *prev = nullptr;//上一次加入答案的节点
        while(node!=nullptr||!st.empty())
        {
            //递归完左区间
            while(node!=nullptr)
            {
                st.push(node);
                node=node->left;
            }
            node=st.top();//取栈顶
            st.pop();//出栈
            //判断是否有右子树
            if(node->right==nullptr||node->right==prev)
            {
                res.push_back(node->val);
                prev=node;//记录每一次加入答案的点,防止重复加入死循环
                node=nullptr;
            }else
            {
                //如果还有右边的节点,就重写把根节点入栈,然后让root指向右节点,让右节点加入答案
                st.push(node);
                node=node->right;
            }
        }
        return res;
    }
};

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

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

相关文章

Baichuan-13B:130亿参数的开源语言模型,引领中文和英文benchmark

Baichuan-13B: 一个强大的开源大规模语言模型 标题&#xff1a;Baichuan-13B&#xff1a;130亿参数的开源语言模型&#xff0c;引领中文和英文benchmark Baichuan-13B是由百川智能开发的一个开源大规模语言模型项目&#xff0c;包含了130亿参数。该模型在中文和英文的权威ben…

基于Nginx深入浅出亿级流量架构设计(更新至2023.7.18)

基于Nginx深入浅出亿级流量架构设计 环境准备/安装部署Nginx四个发行版本简单介绍Nginx的安装 Nginx的目录结构与基本运行原理及其最小配置解析Nginx虚拟主机与域名配置ServerName匹配规则反向代理在系统结构中的应用场景Nginx的反向代理配置基于反向代理的负载均衡器 环境准备…

自洽性改善语言模型中的思维链推理

自洽性改善语言模型中的思维链推理 摘要介绍对多样化路径的自洽实验实验设置主要结果当CoT影响效率时候&#xff0c;SC会有所帮助与现有方法进行比较附加研究 相关工作总结 原文&#xff1a; 摘要 本篇论文提出了一种新的编码策略——自洽性&#xff0c;来替换思维链中使用的…

【STM32】SPI屏幕刷图总结:GPIO模拟,硬件SPI,DMA+硬件SPI

文章目录 GPIO模拟SPI硬件SPI外设DMA硬件SPI外设总结 代码工程&#xff1a;https://github.com/liefyuan/stm32-spi-st7789-tft.git 前言 我的屏幕的分辨率是&#xff1a;240*320 驱动是&#xff1a;ST7789V 线驱动方式&#xff1a;四线SPI&#xff08;CS&#xff0c;DC&#…

206. 反转链表

给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 3&#xff1a; 输入&am…

PageObject+Python+Appium

目录 前言&#xff1a; 简介 功能 常用目录 配置 实例 - 第一次启动 app 实例 - 登录 代码入口实例 结果展示 前言&#xff1a; Page Object模式是一种常用的设计模式&#xff0c;用于组织和管理自动化测试脚本中的页面对象。它将页面的元素和操作封装在一个独立的类…

数据质量相关问题系列

数据质量问题是什么&#xff1f; 数据质量&#xff0c;是指在业务环境下&#xff0c;数据符合数据消费者的使用目的&#xff0c;能满足业务场景具体需求的程度。而数据质量问题是指数据集中存在无法容忍的缺陷&#xff0c;从而降低该数据的可靠性和可信度。简单理解就是数据很脏…

【JAVA】云HIS系统功能菜单知识(一)

一、云HIS特色 云HIS滚动消息栏&#xff1a;质控消息、住院时长、药库结转、患者入院、医嘱停止、新开医嘱、门诊用药不良、出院审核、药品调拨、排班提醒、药品库存、药品过期、药品临期等帮助医生、护士和相关管理人员实时接收院内消息并作出处理。 二、云HIS功能菜单 【预约…

【MySQL】MySQL事务保姆级教程(适合MySQL初学者学习)

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;重…

桥接模式——连接抽象维度和实现维度

桥接模式 bridge pattern 一 定义 桥接模式&#xff0c;是将抽象部分与它的具体实现部分分离&#xff0c;使它们都可以独立地变化。它是将两个不同的维度建立联系。这两个维度通常是指&#xff1a;抽象维度和实现维度。 使用场景 在抽象和具体实现之间需要增加更多的灵活性…

找不到dll的问题解决,loadlibrary 126错误

症状&#xff1a;loadlibrary时&#xff0c;getlasterror返回126&#xff0c;表示是找不到模块错误。来到目录下看到依赖所需的dll均存在 解决办法&#xff1a; 1.下载ProcmonProcmom-监视进程所有的动作资源-CSDN文库https://download.csdn.net/download/gergul/880597622.运…

RocketMQ第四节(部署模式、监控面板等)

1&#xff1a;mq的部署模式 部署方式 | RocketMQ 参考官网。 单机模式&#xff1a;抗风险能力差&#xff0c;单机挂机没服务&#xff0c;单机硬盘损坏&#xff0c;丢失数据 多机&#xff08;多master没有Slave副本&#xff09;: 多个master采用RAID10磁盘&#xff0c;不会丢…

[java安全]类加载器CommonsCollections3

文章目录 【java安全】类加载器&CommonsCollections3前言java类加载器URLClassLoader利用ClassLoader#defineClass()直接加载字节码 使用TemplatesImpl加载字节码TemplatesImpl中字节码实现AbstractTranslet类 构造未完成POCTrAXFilter类调用newTransformer()InstantiateTr…

【AI绘画Stable Diffusion】高清图像放大+面部修复+一键抠图,谈谈你可能不知道的优化技巧!

一提起后期处理&#xff0c;我猜你可能立马想到的就是图像放大。 今天&#xff0c;我要向你展示的后期处理手法&#xff0c;以及优化技巧。 图片放大算法 如果你常用的是秋叶大佬的整合包&#xff0c;那么你对"R-ESRGAN 4x"和"R-ESRGAN 4x Anime6B"应该…

FPGA单端口RAM——IP核

文章目录 前言一、RAM简介1、随机存储器IP核分类1、RAM IP核2、ROM IP核 2、RAM IP核 二、IP核配置步骤三、源码1、ram_rw驱动文件2、ip_1port_ram顶层文件3、仿真文件4、仿真波形 四、SignalTap II在线调试五、总结六、参考资料 前言 环境&#xff1a; 1、Quartus18.1 2、vsco…

Leetcode-每日一题【1019.链表中的下一个更大结点】

题目 给定一个长度为 n 的链表 head 对于列表中的每个节点&#xff0c;查找下一个 更大节点 的值。也就是说&#xff0c;对于每个节点&#xff0c;找到它旁边的第一个节点的值&#xff0c;这个节点的值 严格大于 它的值。 返回一个整数数组 answer &#xff0c;其中 answer[…

Servlet的监听器

Servlet常用的监听器 ServletContextAttributeListener 用来感知ServlerContext对象属性变化&#xff0c;比如添加或删除属性变化 ServletContextListener 用来感知ServlerContext对象的创建和销毁的 ServletRequestListener 可以用来监听感知ServletRequest对象的创建和销毁的…

大语言模型(LLM)评估综述

论文地址&#xff1a;https://arxiv.org/abs/2307.03109 Github地址&#xff1a;https://github.com/MLGroupJLU/LLM-eval-survey 一 、背景 随着ChatGPT和GPT-4的推出&#xff0c;大型语言模型&#xff08;LLM&#xff09;在学术界和工业界的热度日益升高&#xff0c;这主要…

优雅的设计测试用例

⭐️前言⭐️ 入职以后接触到了公司的具体业务&#xff0c;提升了设计测试用例的能力&#xff0c;于是沉淀出这篇文档与大家分享。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主将持续更新学习记录收获&…

Acwing.905 区间选点(贪心)

题目 给定N个闭区间[a,bi]&#xff0c;请你在数轴上选择尽量少的点&#xff0c;使得每个区间内至少包含一个选出的点。输出选择的点的最小数量。 位于区间端点上的点也算作区间内。 输入格式 第一行包含整数N&#xff0c;表示区间数。 接下来N行&#xff0c;每行包含两个整数…