【二叉树经典题目】

根据二叉树创建字符串

本题的关键在于什么情况要省略括号,什么情况不能省略:

  1.  左右为空可以省略括号
  2.  左不为空,右为空可以省略括号
  3. 左为空,右不为空不能省略括号
class Solution {
public://1.左右为空可以省略括号//2.左不为空,右为空可以省略括号//3.左为空,右不为空不能省略括号string tree2str(TreeNode* root) {string ret;if (root == nullptr){return ret;}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;}
};

 

二叉树的层序遍历

层序遍历咋一看很简单,借助队列就能实现,但是关键在于如何把每一层的结点区分开来,放到不同的vector中。这里采用的方法是使用一个计数器count来记录本层的结点数,count减到0时,本层结点已经遍历完了,此时队列中的就是下一层的结点,用队列的元素个数更新count即可。 

class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> ret;if (root == nullptr){return ret;}queue<TreeNode*> q;q.push(root);int count = 1;//记录本层的结点数while (!q.empty()){vector<int> v;while (count--){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);}}ret.push_back(v);count = q.size();}return ret;}
};

 

 二叉树的最近公共祖先

思路:找到根结点到p结点和q结点的路径,对比两个路径即可得到答案,时间复杂度O(n)

class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {vector<TreeNode*> pPath;vector<TreeNode*> qPath;findPath(root, p, pPath);findPath(root, q, qPath);int minSize = min(pPath.size(), qPath.size());int i = 0;for (; i < minSize; i++){if (pPath[i] != qPath[i]){break;}}return pPath[i - 1];}bool findPath(TreeNode* root, TreeNode* target, vector<TreeNode*>& path){if (root == nullptr){return false;}path.push_back(root);if (root == target){return true;}if (findPath(root->left, target, path)){return true;}if (findPath(root->right, target, path)){return true;}//回溯--恢复现场path.pop_back();return false;}
};

二叉搜索树转双向链表

要将二叉搜索树转换为有序的双向链表,那么肯定与中序遍历有关。中序遍历过程中用prev记录上一个遍历的结点,cur记录当前结点,prev->right = cur, cur->left = prev即可。

class Solution {
public:void InOrderConvert(TreeNode* cur, TreeNode*& prev){if (cur == nullptr){return;}InOrderConvert(cur->left, prev);cur->left = prev;if (prev){prev->right = cur;}prev = cur;InOrderConvert(cur->right, prev);}TreeNode* Convert(TreeNode* pcurOfTree) {TreeNode* ret = pcurOfTree;if (ret == nullptr){return ret;}//找到最小的结点--也就是重新链接后双链表的头结点while (ret->left){ret = ret->left;}TreeNode* prev = nullptr;InOrderConvert(pcurOfTree, prev);return ret;}
};

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

手工构建二叉树很容易,本题难点在于如何将手动构建的过程转化成代码。想一想手工构建的过程。如何确定二叉树的根结点?看前序序列。如何确定左右子树有哪些结点?看中序序列。如何构建左右子树呢? 先确定根节点,这样又转化成了第一个问题。

牢记这一点:前序序列确定根结点。,中序序列确定左右子树有哪些结点。怎么表示左右子树的结点?用一个区间标识范围即可。

class Solution {
public:TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int inbegin, int inend){//中序序列区间不存在if (inbegin > inend){return nullptr;}//根据前序序列确定根结点TreeNode* root = new TreeNode(preorder[prei]);prei++;//根据中序序列确定左右子树的结点int pos = inbegin;while (pos <= inend) {if (root->val == inorder[pos]){break;}pos++;}//[inbegin, pos - 1] [pos + 1, inend]root->left = _buildTree(preorder, inorder, prei, inbegin, pos - 1);root->right = _buildTree(preorder, inorder, prei, pos + 1, inend);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int prei = 0;return _buildTree(preorder, inorder, prei, 0, inorder.size()- 1);}
};

根据后序序列和中序序列构建二叉树

思路和上一题类似,不过要注意postorder要从后往前遍历,并且要先构建右子树,再构建左子树。 

class Solution {
public:TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {int posti = postorder.size() - 1;return _buildTree(inorder, postorder, posti, 0, inorder.size() - 1);}TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& posti, int inbegin, int inend){if (inbegin > inend){return nullptr;}//后序序列确定根节点TreeNode* root = new TreeNode(postorder[posti]);posti--;//中序序列确定左右子树int pos = inbegin;while (pos <= inend){if (root->val == inorder[pos]){break;}pos++;}//[inbegin, pos - 1] [pos + 1, inend]//构建右子树和左子树root->right = _buildTree(inorder, postorder, posti, pos + 1, inend);root->left = _buildTree(inorder, postorder, posti, inbegin, pos - 1);return root;}
};

二叉树的前序遍历迭代版本

 递归写法很简单,如何用迭代来完成呢?

将一颗树分为左路结点和左路结点的右子树,遍历到一颗树的左路结点时就访问它,并将它放入栈中,继续访问下一个左路结点。当左路结点访问完后,从栈中取结点,该结点就是最近的一个左路结点,接下来访问它的右子树,又回到了最开始的步骤。

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {stack<TreeNode*> st;vector<int> ret;TreeNode* cur = root;while (cur || !st.empty()){//遍历左路结点并访问while (cur){ret.push_back(cur->val);st.push(cur);cur = cur->left;}//遍历左路结点的右子树TreeNode* top = st.top();cur = top->right;st.pop();}return ret;}
};

二叉树的中序遍历迭代版本

和上一题类似,一颗树看作左路结点和左路结点的右子树,和前序遍历不同的是,结点的访问时机不同。前序遍历是遍历到某个结点时就要访问它,而中序遍历,遍历到某个结点时先不访问,将它存入栈中,当再把它从栈中取出来时,代表它的左子树已经访问过了,接下来要访问右子树,此时就是访问该结点的时机。

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {stack<TreeNode*> st;vector<int> ret;TreeNode* cur = root;while (cur || !st.empty()){//遍历左路结点while (cur){st.push(cur);cur = cur->left;}TreeNode* top = st.top();st.pop();//访问左路结点ret.push_back(top->val);//遍历左路结点的右子树cur = top->right;}return ret;}
};

二叉树的后序遍历迭代版本

同样,一棵树分为左路结点和左路结点的右子树。左路结点的访问访问时机和前两道题又有所不同。遍历到左路结点时将它存入栈中,不能访问,当它的左子树遍历完时,从栈中取出,此时还是不能访问,要取遍历它的右子树,访问完右子树后,再次从栈中取出,此时才能访问并出栈。

本题难点在于,当你把结点从栈中取出来时,你如何知道它的右子树是否已经访问了呢?此时应该访问这个结点,还是暂时不访问,去遍历它的右子树呢?有两种方案:

1.设置flag值标记结点的右子树是否已经访问。

要让每一个结点都对应一个flag值,怎么做到呢?很简单,再建立一个栈,类型为bool,当结点入栈时,bool类型的变量也跟着入栈(注意不是同一个栈),初始值为false。当从栈中取出结点时,也把对应的bool变量取出,如果为false,说明右子树还没有遍历,将bool变量置为true,访问右子树;若为true,说明右子树已经遍历,访问结点并出栈,同时将bool变量也出栈。结点入栈,bool变量入栈;结点出栈,bool变量也出栈,这样一来就能做到一一对应了。

lass Solution {
public:vector<int> postorderTraversal(TreeNode* root) {stack<TreeNode*> st;stack<bool> flag;vector<int> ret;TreeNode* cur = root;while (cur || !st.empty()){//遍历左路结点while (cur){st.push(cur);flag.push(false);cur = cur->left;}TreeNode* top = st.top();if (top->right == nullptr || flag.top() == true){//访问左路结点ret.push_back(top->val);st.pop();flag.pop();}else{//遍历左路结点的右子树flag.top() = true;cur = top->right;}}return ret;}
};

2.标记前一个访问的结点

如果一个结点的右子树 (非空) 遍历完了,那么上一个访问的结点一定是该结点的右孩子。根据这一规律,只需创建一个prev变量来记录上一个访问的结点。如果一个结点从栈中取出来,若该结点右子树为空,则可以直接访问;若该结点右子树不为空且上一个访问的结点是该结点的右孩子,则也可以访问;否则就要先访问该结点的右子树。

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> ret;stack<TreeNode*> st;TreeNode* cur = root;TreeNode* prev = nullptr;while (cur || !st.empty()){//遍历左路结点while (cur){st.push(cur);cur = cur->left;}TreeNode* top = st.top();if (top->right == nullptr || top->right == prev){//访问左路结点ret.push_back(top->val);st.pop();prev = top;}else{//遍历右子树cur = top->right;}}return ret;}
};

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

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

相关文章

基于SSM的高校疫情防控出入信息管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

提高微星笔记本Linux下散热性能,MSI-EC 驱动新补丁发布

导读近日消息&#xff0c;今年早些时候&#xff0c;Linux 6.4 中添加了 MSI-EC 驱动程序&#xff0c;允许对 Linux 系统微星笔记本电脑进行更多控制。 MSI-EC 驱动程序近日迎来新补丁&#xff0c;为微星笔记本带来 Cooler Boost 功能。该功能允许提高笔记本电脑的风扇转速&…

从云到AI到大模型,阿里云始终和谁站在了一起?

引言&#xff1a;云的创新没有减缓 而且在加速深入百行百业 【科技明说 &#xff5c; 热点关注】 【全球云观察 | 每日看点】作为中国最大的云计算厂商&#xff0c;阿里云的产品矩阵覆盖越来越全面&#xff0c;越来越细致&#xff0c;越来越到位。 ​ 在2023杭州云栖大会现场…

java项目之医院病历管理系统(ssm框架)

项目简介 医院病历管理系统实现了以下功能&#xff1a; 管理员&#xff1a;个人中心、医院公告管理、用户管理、科室信息管理、医生管理、出诊信息管理、预约时间段管理、预约挂号管理、门诊病历管理、就诊评价管理、系统管理。医生&#xff1a;个人中心、出诊信息管理、预约…

企业计算机电脑中了locked勒索病毒怎么办,勒索病毒解密,数据恢复

网络技术的不断发展&#xff0c;为我们的企业带来了很大的便利&#xff0c;大部分企业都会选择合适的办公软件系统&#xff0c;方便自身的生产与运营。近期&#xff0c;网络上的locked勒索病毒又开始攻击企业的计算机服务器了&#xff0c;经过10月份云天数据恢复中心对企业数据…

02【Git分支的使用、Git回退、还原】

上一篇&#xff1a;01【Git的基本命令、底层命令、命令原理】 下一篇&#xff1a;03【Git的协同开发、TortoiseGit、IDEA的操作Git】 文章目录 02【Git分支的使用、Git回退、还原】一、分支1.1 分支概述1.1.1 Git分支简介1.1.2 Git分支原理 1.2 创建分支1.2.1 创建普通分支1.…

GPT做SQL查询引擎的自然语言

目录 面向企业查询的生成式人工智能 步骤1&#xff1a;将示例数据转换为单字符字符串 步骤2&#xff1a;为大型语言模型&#xff08;LM&#xff09;创建提示符 步骤3&#xff1a;将数据发送到OpenAI的API 步骤4&#xff1a;执行GPT返回的SQL代码的结果 步骤5(可选)&#…

iPhone没有信号无法付钱?这样的损友要远离

近段时间以来&#xff0c;网络频传iPhone的信号差&#xff0c;导致朋友外出吃饭&#xff0c;用iPhone的朋友无法付钱&#xff0c;而只好拿国产手机的自己付钱&#xff0c;如果遇上这样的朋友只能说你被骗了&#xff0c;那是朋友在坑你&#xff0c;这样的朋友绝对是损友&#xf…

796. 子矩阵的和(二维前缀和)

题目&#xff1a; 796. 子矩阵的和 - AcWing题库 思路&#xff1a; 1.暴力搜索&#xff08;搜索时间复杂度为O(n2)&#xff0c;很多时候会超时&#xff09; 2. 前缀和&#xff08;左上角&#xff08;二维&#xff09;前缀和&#xff09;&#xff1a;本题特殊在不是直接求前…

systrace/perfetto如何看surfaceflinger的vsync信号方法-android framework实战车载手机系统开发

背景&#xff1a; hi&#xff0c;粉丝朋友们&#xff1a; 大家好&#xff01;近期分享了surfaceflinger相关的一些blog&#xff0c;有同学就对相关的一些内容产生了一些疑问。 比如&#xff1a;vsync查看问题&#xff0c;即怎么才可以说是vsync到来了。 比如perfetto中surfac…

【MySql】10- 实践篇(八)

文章目录 1. 用动态的观点看加锁1.1 不等号条件里的等值查询1.2 等值查询的过程1.3 怎么看死锁&#xff1f;1.4 怎么看锁等待&#xff1f;1.5 update 的例子 2. 误删数据后怎么办?2.1 删除行2.2 误删库/表2.3 延迟复制备库2.4 预防误删库 / 表的方法2.4.1 账号分离2.4.2 制定操…

coturn服务器的搭建

Window下搭建coturn服务器&#xff1a; 准备材料&#xff1a; 1、安装Cygwin&#xff0c;地址&#xff1a;https://cygwin.com/install.html 由于Window无法直接部署coturn&#xff0c;因此需要下载安装Cygwin在Window上部署Linux虚拟环境。 在安装的时候需要安装几下packe…

顺序表练习

顺序表练习 图解插入与删除&#xff0c;详见相关内容&#xff1a;顺序存储结构的插入与删除 //顺序表的定义、创建、插入、删除、查找 //定义&#xff1a;结构体中数组、表长 //创建:输入元素&#xff0c;表长 //插入&#xff1a;判断表是否已满、判断位序合法性 //插入位序k…

【Amazon】跨AWS账号资源授权存取访问

文章目录 一、实验框架图二、实验过程说明三、实验演示过程1、在A账号中创建S3存储桶2、在A账号创建S3存储桶访问策略3、在A账号创建信任开发账号的角色4、在B账号为用户添加内联策略5、在B账号中切换角色&#xff0c;以访问A账号中的S3资源 四、实验总结 一、实验框架图 本次…

Python通过pyecharts对爬虫房地产数据进行数据可视化分析(一)

一、背景 对Python通过代理使用多线程爬取安居客二手房数据&#xff08;二&#xff09;中爬取的房地产数据进行数据分析与可视化展示 我们爬取到的房产数据&#xff0c;主要是武汉二手房的房源信息&#xff0c;主要包括了待售房源的户型、面积、朝向、楼层、建筑年份、小区名称…

华为数通方向HCIP-DataCom H12-831题库(多选题:101-120)

第101题 LSR对收到的标签进行保留,且保留方式有多种,那么以下关于LDP标签保留一自由方式的说法 A、保留邻居发送来的所有标签 B、需要更多的内存和标签空间 C、只保留来自下一跳邻居的标签,丢弃所有非下一跳铃邻居发来的标签 D、节省内存和标签空间 E、当IP路由收敛、下一跳…

客户端性能测试基础知识

目录 1、客户端性能 1.1、客户端性能基础知识 2、客户端性能工具介绍与环境搭建 2.1.1、perfdog的使用 2.1.2、renderdoc的使用 1、客户端性能 1.1、客户端性能基础知识 客户端性能知识这里对2D和3D类游戏进行展开进行&#xff0c;讲述的有内存、CPU、GPU、帧率这几个模块…

【计算机网络】分层模型和应用协议

网络分层模型和应用协议 1. 分层模型 1.1 五层网络模型 网络要解决的问题是&#xff1a;两个程序之间如何交换数据。 四层&#xff1f;五层&#xff1f;七层&#xff1f; 2. 应用层协议 2.1 URL URL&#xff08;uniform resource locator&#xff0c;统一资源定位符&#…

leetCode 136.只出现一次的数字 + 位运算

136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算…

保护生产中 Node.js 应用程序安全的 15 项最佳实践

在后端开发方面&#xff0c;Node.js 是开发人员最喜欢的技术之一。它的受欢迎程度不断上升&#xff0c;现已成为在线攻击的主要目标之一。这就是为什么保护 Node.js 免受漏洞和威胁至关重要。 在本指南中&#xff0c;您将看到为生产设计安全 Node.js 应用程序架构的 15 种最佳…