当前位置: 首页 > article >正文

c++——二叉树进阶

1. 内容安排说明

二叉树在前面C数据结构阶段已经讲过,本节取名二叉树进阶是因为:

1. map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构

2. 二叉搜索树的特性了解,有助于更好的理解map和set的特性

3. 二叉树中部分面试题稍微有点难度,在前面讲解大家不容易接受,且时间长容易忘

4. 有些OJ题使用C语言方式实现比较麻烦,比如有些地方要返回动态开辟的二维数组,非常麻 烦。 因此本节借二叉树搜索树,对二叉树部分进行收尾总结。

2. 二叉搜索树实现

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

1若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

2若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

3它的左右子树也分别为二叉搜索树

2.1二叉搜索树操作

接下来我们对于搜索二叉树的分析都是基于这个树来进行的

1二叉树的搜索

Node* Find(const K& key)
{if (_root->_key == key){return _root;}Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return cur;}}return nullptr;
}

2二叉树的构建

bool Insert(const K& key)
{if (_root == nullptr){_root = new Node(key);return true;}//先找到符合的位置,再进行插入Node* prev = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){prev = cur;cur = cur->_right;}else if (cur->_key > key){prev = cur;cur = cur->_left;}else{return false;//搜索二叉树不允许重复}}//这时候找到位置了进行链接cur = new Node(key);if (prev->_key < key){prev->_right = cur;}else{prev->_left = cur;}return true;}

3二叉树的删除

这里的删除分为三个场景

1删除的节点为叶子节点(没有子节点)(这里直接删除就行了)

2删除的节点有一个子节点

3删除的节点有两个子节点

这里就要用到替换法(左树的最大节点或者右树的最小节点)

具体代码:

bool Erase(const K& key){//这里要保留parent 就不复用Find函数了Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else//找到了{//接下来根据各个场景依次判断//如果左右其中一个为空,那么属于第一类,直接将空的另一边连接到parent//左为空if (cur->_left == nullptr){if (cur == _root)//防止parent为空{_root = cur->_right;}else{if (parent->_key < cur->_key){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}//右为空else if (cur->_right == nullptr){if (cur == _root)//防止parent为空{_root = cur->_left;}else{if (parent->_key < cur->_key){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}}else//左右都不为空,我们找左树最大的来替代{parent = cur;//防止parent 为空Node* LeftMax = cur->_left;while (LeftMax->_right){parent = LeftMax;LeftMax = LeftMax->_right;}//找到最大的就替换cur->_key = LeftMax->_key;//再将这个节点删除,这个节点可能有左树if (LeftMax == cur->_left){parent->_left = LeftMax->_left;}else//这时候要特殊处理{parent->_right = LeftMax->_left;}cur = LeftMax;}delete cur;return true;}}//没找到return false;}

接下来我们用递归实现二叉搜索树的递归版本

1查找

public://递归版本的查找Node* FindR(const K& key){return _FindR(_root, key);}
private:
Node* _FindR(Node* root,const K& key)
{if (root == nullptr){return nullptr;}if (root->_key == key){return root;}else if (root->_key < key){return _FindR(root->_right, key);}else{return _FindR(root->_left, key);}}

2插入

插入这里就要讲的很多了,这里_InsertR用的是引用返回(这是指针和引用的结合),这就给我们省下了很多力气,因为不用再去判断这个节点是parent 的左树还是右树

public:	
//递归版本的插入bool InsertR(const K& key){return _InsertR(_root, key);}
private:bool _InsertR(Node* &root, const K& key){if (root == nullptr){//这时候要注意,指针加引用很厉害root = new Node(key);return true;}if (root->_key < key){_InsertR(root->_right, key);}else if (root->_key > key){_InsertR(root->_left, key);}else{return false;}}

3删除

bool _EraseR(Node*& root, const K& key)
{if (root == nullptr){return false;}//要转化为子问题分置if (root->_key < key){_EraseR(root->_right, key);}else if (root->_key > key){_EraseR(root->_left, key);}else //找到了{Node* del = root;if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else //3左右都不为空{Node* leftMax = root->_left ;while (leftMax->_right){leftMax = leftMax->_right;}swap(leftMax->_key, root->_key);//这一步很精髓return _EraseR(root->_left, key);这里一定是返回了,不然还会走下面删除,就删除多了}delete del;return true;}
}

这里最后_EraseR的时候不能传leftMax,因为传leftMax有些情况删不掉

以上三个基本的完成了,剩下的destory走一个后续删除就可以了

3.二叉树搜索树应用分析

二叉搜索树在实际应用当中很常见

有两个模型

1key的搜索模型:快速判断不在的场景

门禁系统,小区车辆出入系统

2,key.value的搜索模型:通过一个值,去找另外一个值

商场的车辆出入系统模型(车牌号和入场时间联系起来)

4. 二叉树进阶面试题

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

这一题根本就是前序

要注意

1当左树为空,右树为空,都不加括号

2左树不为空,右树为空,右树不加括号

3左树为空,右树不为空,左树加括号

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

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

class Solution {
public:bool Find(TreeNode* root,int val){if(root==nullptr)return false;if(root->val==val)return true;return Find(root->left,val) || Find(root->right,val);}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(root==nullptr)return nullptr;if(p==root || q == root){return root;}bool pleft,pright,qleft,qright;pleft = Find(root->left,p->val);pright = !pleft;qleft = Find(root->left,q->val);qright = !qleft;if(pleft && qleft){return lowestCommonAncestor( root->left,  p,  q);}else if(pright && qright){return lowestCommonAncestor( root->right,  p,  q);}else{return root;}}};

这里的解法就是暴力求解,我们可以优化解法让它达到O(N)

就是如果能倒着走,就可以转化为链表相交

这里就用一个栈来实现

class Solution {
public:bool CreatStack(TreeNode* root,stack<TreeNode*>& st,TreeNode* x){if(root==nullptr)return false;st.push(root);if(root==x){return true;}//左右两边找到了就返回if(CreatStack(root->left,st,x))return true;if(CreatStack(root->right,st,x))return true;st.pop();return false;}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {stack<TreeNode*> pstack,qstack;CreatStack(root,pstack,p);CreatStack(root,qstack,q);while(pstack.size()>qstack.size()){pstack.pop();}while(pstack.size()<qstack.size()){qstack.pop();}while(pstack.top()!=qstack.top()){pstack.pop();qstack.pop();}return pstack.top();}
};

二叉搜索树与双向链表_牛客题霸_牛客网

这种写法是实在写不出来就这样写

class Solution {
public:void Inorder(TreeNode* root,vector<TreeNode*> &v){if(root==nullptr){return ;}Inorder(root->left,v);v.push_back(root);Inorder(root->right,v);}TreeNode* Convert(TreeNode* pRootOfTree) {if(pRootOfTree==nullptr){return nullptr;}vector<TreeNode*> vt;Inorder(pRootOfTree,vt);TreeNode* root = vt[0];TreeNode* end = vt[vt.size()-1];root->right = vt[1];end->left = vt[vt.size()-2];for(size_t i  = 1;i<vt.size()-1;i++){vt[i]->left = vt[i-1];vt[i]->right = vt[i+1];}root->left = nullptr;end->right = nullptr;return root;}
};

这个才是正解 

class Solution {
public:void _Convert(TreeNode* cur,TreeNode* &prev){if(cur==nullptr){return ;}_Convert(cur->left,prev);//中序cur->left = prev;if(prev){prev->right = cur;}prev = cur;_Convert(cur->right,prev);}TreeNode* Convert(TreeNode* pRootOfTree) {if(pRootOfTree ==nullptr){return nullptr;}TreeNode* head = pRootOfTree;TreeNode* prev = nullptr;while(head->left){head = head->left;}_Convert(pRootOfTree, prev);head->left==nullptr;return head;}
};

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

前序:根       左子树          右子树

中序:左子树       根        右子树

class Solution {
public:TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,int& i,int begin,int end){if(begin>end){return nullptr;}//建立根节点TreeNode* root = new TreeNode(preorder[i]);//查找根节点位置,分出左右区间int rooti = begin;while(rooti<=end){if(preorder[i]==inorder[rooti]){break;}rooti++;}i++;//区间就是[begin,rooti-1]rooti[rooti+1,end]root->left = _buildTree(preorder,inorder,i,begin,rooti-1);root->right = _buildTree(preorder,inorder,i,rooti+1,end);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int i = 0;int begin = 0;int end = inorder.size()-1;TreeNode* root = _buildTree(preorder,inorder,i,begin,end);return root;}
};

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

这两题就差不多,就是后续的时候倒着遍历posorder

class Solution {
public:TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder,int& posi,int begin ,int end){if(begin>end){return nullptr;}TreeNode* root = new TreeNode(postorder[posi]);int rooti = begin;while(rooti<=end){if(inorder[rooti]==postorder[posi])break;rooti++;}posi--;root->right = _buildTree(inorder,postorder,posi,rooti+1,end);root->left = _buildTree(inorder,postorder,posi,begin,rooti-1);return root;}TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {int posi = postorder.size()-1;int begin = 0;int end = inorder.size()-1;TreeNode* root = _buildTree(inorder,postorder,posi,begin,end);return root;}
};

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

这个要求是非递归实现,这就与我们之前搞的不同了,递归实现很简单,非递归就是用迭代的方式来实现

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {TreeNode* cur = root;stack<TreeNode*> st;vector<int> v;while(cur || !st.empty()){//1先访问左树节点while(cur){st.push(cur);v.push_back(cur->val);cur = cur->left;}//这里左树为空了cur = st.top();st.pop();//以子问题的方式访问右树cur = cur->right;}return v;}
};

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

这一题复制上一题的模板,就是在将要访问右树的时候在push_back;

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

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {TreeNode* cur = root;stack<TreeNode*> st;TreeNode* prev = nullptr;vector<int> v;while(cur || !st.empty()){//1先访问左树节点while(cur){st.push(cur);cur = cur->left;}//cur是指要取访问的节点//top取出来判断后决定cur的指向TreeNode* top = st.top();if(top->right==nullptr || top->right == prev){//这时候不用访问prev = top;v.push_back(top->val);st.pop();}else{cur =top->right;}}return v;}
};

相关文章:

c++——二叉树进阶

1. 内容安排说明 二叉树在前面C数据结构阶段已经讲过&#xff0c;本节取名二叉树进阶是因为&#xff1a; 1. map和set特性需要先铺垫二叉搜索树&#xff0c;而二叉搜索树也是一种树形结构 2. 二叉搜索树的特性了解&#xff0c;有助于更好的理解map和set的特性 3. 二叉树中部…...

MySQL 中如何进行 SQL 调优?

在MySQL中进行SQL调优是一个系统性工程&#xff0c;需结合索引优化、查询改写、性能分析工具、数据库设计及硬件配置等多方面策略。以下是具体优化方法及案例说明&#xff1a; 一、索引优化&#xff1a;精准提速的关键 索引类型选择 普通索引&#xff1a;加速频繁查询的列&…...

【C++游戏引擎开发】第33篇:物理引擎(Bullet)—射线检测

一、射线检测核心理论体系 1.1 射线检测的数学基础 1.1.1 参数化射线方程 射线在三维空间中的数学表达采用参数方程: r ( t ) = o + t d ^ ( t ∈ [...

基于flask+pandas+csv的报表实现

基于大模型根据提示词去写SQL执行SQL返回结果输出报表技术上可行的&#xff0c;但为啥还要基于pandas去实现呢&#xff1f; 原因有以下几点&#xff1a; 1、大模型无法满足实时性输出报表的需求&#xff1b; 2、使用大模型比较适合数据量比较大的场景&#xff0c;大模型主要…...

PySide6 GUI 学习笔记——常用类及控件使用方法(常用类字体QFont)

文章目录 一、QFont常用方法二、常用方法总结1. 基础属性设置2. 高级样式控制3. 序列化与反序列化4. 字体信息获取 三、应用实例 字体类QFont用于设置界面控件上显示的字体&#xff0c;它包含字体名称、字体尺寸、粗体字、斜体字、删除线、上划线、下划线、字体间距等属性。 如…...

北斗导航 | 北斗RTK定位厄待解决的问题,未来发展

北斗RTK(实时动态定位)技术在高精度定位领域已取得显著进展,但仍面临一系列技术挑战和未来发展方向。以下从亟待解决的问题和未来发展趋势两方面进行详细分析: 一、北斗RTK定位亟待解决的核心问题 电离层扰动对定位精度的影响 电离层扰动是当前北斗RTK技术面临的最大自然挑…...

宝塔服务安装使用的保姆级教程

宝塔介绍&#xff1a; 宝塔面板&#xff08;BT Panel&#xff09; 是一款 国产的服务器运维管理面板&#xff0c;主要用于简化 Linux/Windows 服务器的网站、数据库、FTP、防火墙等管理操作。它通过图形化界面&#xff08;Web端&#xff09;和命令行工具&#xff08;bt 命令&a…...

Linux平台下SSH 协议克隆Github远程仓库并配置密钥

目录 注意&#xff1a;先提前配置好SSH密钥&#xff0c;然后再git clone 1. 检查现有 SSH 密钥 2. 生成新的 SSH 密钥 3. 将 SSH 密钥添加到 ssh-agent 4. 将公钥添加到 GitHub 5. 测试 SSH 连接 6. 配置 Git 使用 SSH 注意&#xff1a;先提前配置好SSH密钥&#xff0c;然…...

Electron 打包与发布指南:让你的应用运行在 Windows、macOS、Linux

&#x1f680; Electron 打包与发布指南&#xff1a;让你的应用运行在 Windows、macOS、Linux 使用 Electron 开发桌面应用只是第一步&#xff0c;最终我们还需要将应用打包成用户可运行的可执行文件&#xff08;如 .exe、.dmg、.AppImage&#xff09;&#xff0c;并能在各平台…...

Java【网络原理】(5)深入浅出HTTPS:状态码与SSL/TLS加密全解析

目录 1.前言 2.正文 2.1状态码 2.2HTTP与HTTPS的关系 2.3SSL协议 2.3.1对称加密 2.3.2非对称加密 2.3.3中间人攻击 2.3.4校验机制 2.3.4.1证书 2.3.4.2数字签名 1. 数字签名的生成过程 2. 数字签名的验证过程 2.4TLS协议&#xff08;握手过程&#xff09; 3.小结…...

【基础IO下】磁盘/软硬链接/动静态库

前言&#xff1a; 文件分为内存文件和磁盘文件。磁盘文件是一个特殊的存在&#xff0c;因为磁盘文件不属于冯诺依曼体系&#xff0c;而是位于专门的存储设备中。因此&#xff0c;磁盘文件存在的意义是将文件更好的存储起来&#xff0c;一边后续对文件进行访问。在高效存储磁盘…...

SpringBoot项目容器化进行部署,meven的docker插件远程构建docker镜像

需求&#xff1a;将Spring Boot项目使用容器化进行部署 前提 默认其他环境,如mysql,redis等已经通过docker部署完毕, 这里只讨论,如何制作springboot项目的镜像 要将Spring Boot项目使用docker容器进行部署&#xff0c;就需要将Spring Boot项目构建成一个docker镜像 一、手动…...

【小记】excel vlookup一对多匹配

一个学生报四门课&#xff0c;输出每个学生课程 应用概述操作预处理数据计数指令 COUNTIFS进行一对多匹配 vlookup 应用概述 应用场景&#xff1a;学生报名考试&#xff0c;需要整理成指定格式&#xff0c;发给考试院。 一个学生最多报考四门 格式实例&#xff1a;准考证号 …...

LeetCode热题100 两数之和

目录 两数之和题目解析方法一暴力求解代码 方法二哈希代码 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x1f978; C语言 &#x1f43f;️&#x1f43f;️&#x1f43f;…...

问题解决思路:numpy:DLL load failed

首先明确几点&#xff1a; 1这是在使用anaconda中除开base环境下其他envs时出现的问题 2这不是pytorch版本过高的问题&#xff08;也可以是&#xff09; 3这不是pytorch安装错误的问题&#xff08;也可以是&#xff09;需要检查是否正确安装 解决思路&#xff1a; 本人遇到…...

[春秋云镜] Brute4Road 仿真场景

文章目录 靶标介绍&#xff1a;知识点约束性委派攻击 外网redis主从复制base64提权 内网搭建代理wpcargo插件漏洞mssql弱口令SweetPotato提权远程桌面连接mimikatz抓取hash约束性委派攻击 参考文章 靶标介绍&#xff1a; Brute4Road是一套难度为中等的靶场环境&#xff0c;完成…...

adb 实用命令汇总

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 基础adb命令 # 重启adb adb kill-server# 查看已连接的设备 adb devices# 进入命令行 adb shell# 使用 -s 参数来指定设备 adb -s <设备序列号> shell…...

鸿蒙系统使用ArkTS开发语言支持身份证阅读器、社保卡读卡器等调用二次开发SDK

har库导入&#xff1a; { "license": "", "devDependencies": {}, "author": "", "name": "entry", "description": "Please describe the basic information.", &qu…...

《Python星球日记》 第54天:卷积神经网络进阶

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、深度CNN架构解析1. LeNet-5&#xff08;1998&#xff09;2. AlexNet&#x…...

《AI大模型应知应会100篇》第53篇:Hugging Face生态系统入门

第53篇&#xff1a;Hugging Face生态系统入门 ——从模型获取到部署的全流程实战指南 &#x1f4cc; 摘要 在人工智能快速发展的今天&#xff0c;Hugging Face已成为自然语言处理&#xff08;NLP&#xff09;领域最具影响力的开源平台之一。它不仅提供丰富的预训练模型、强大…...

【基于 LangChain 的异步天气查询2】GeoNames实现地区实时气温查询

目录 功能简介 一、创建GeoNames账号 1、进入官网 2、创建账号 二、运行代码 weather_runnable.py main.py 运行结果 功能简介 本文主要通过Langchain&#xff0c;结合GeoNames实现了地区温度的实时查询&#xff0c;并通过GPT-4o对温度进行一段简短的描述。 一、创建Ge…...

嵌入式与物联网:C 语言在边缘计算时代的破局之道

引言 在万物互联的 2025 年&#xff0c;全球物联网设备连接数突破 300 亿台&#xff0c;其中 78% 的嵌入式控制系统仍基于 C 语言开发。这种跨越半个世纪的编程语言&#xff0c;正以新的技术形态在智能汽车、工业物联网、边缘计算等领域重塑竞争力。本文通过三个前沿应用场景&…...

《基于人工智能的智能客服系统:技术与实践》

一、引言 在数字化时代&#xff0c;客户服务已成为企业竞争的关键领域之一。随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;智能客服系统逐渐成为企业提升服务质量和效率的重要工具。智能客服不仅能够快速响应客户咨询&#xff0c;还能通过自然语言处理&am…...

5. HTML 转义字符:在网页中正确显示特殊符号

在 HTML 开发中,我们经常会遇到需要显示特殊字符的情况,比如 <、>、空格或版权符号等。直接输入这些字符可能会导致 HTML 解析错误或显示异常。接下来通过学习 HTML 转义字符(也称为实体字符),将会掌握了如何在网页中正确显示这些特殊符号的方法。 一、为什么需要转…...

基于nodejs + Koa +Nuxt3的订单系统项目实战

以下是一个基于 Node.js Koa Nuxt3 的订单系统项目实战指南&#xff0c;包含关键实现步骤和代码示例&#xff1a; 一、项目架构设计 project/ ├── backend/ # Koa 后端 │ ├── config/ # 配置文件 │ ├── controllers/ # 控制器 │ ├──…...

软件开发者如何转战AI领域

在人工智能&#xff08;AI&#xff09;技术迅猛发展的当下&#xff0c;越来越多的软件工程师开始考虑转型进入AI领域。本文将探讨AI软件行业的现状、所需能力&#xff0c;以及普通软件工程师在转型过程中可以借助的技能和需要补充的知识。 AI软件行业的现状 截至2025年&#…...

服务器数据恢复—硬盘坏道导致EqualLogic存储不可用的数据恢复

服务器存储数据恢复环境&故障&#xff1a; 一台EqualLogic某型号存储中有一组由16块SAS硬盘组建的RAID5阵列。上层采用VMFS文件系统&#xff0c;存放虚拟机文件&#xff0c;上层一共分了4个卷。 磁盘故障导致存储不可用&#xff0c;且设备已经过保。 服务器存储数据恢复过程…...

JAVA实战开源项目:智能学习平台系统 (Vue+SpringBoot) 附源码

本文项目编号 T 181 &#xff0c;文末自助获取源码 \color{red}{T181&#xff0c;文末自助获取源码} T181&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...

Linux系统使用vscode格式化shell脚本

推荐工具及配置方案 BeautySH 特点 纯 Python 实现&#xff0c;轻量级且活跃维护。 配置步骤 安装 BeautySH pip3 install beautyshVSCode 集成 打开命令面板&#xff08;CtrlShiftP&#xff09;&#xff0c;输入 Tasks: Configure Task&#xff0c;选择 Create tasks.json f…...

牛客练习赛138

目录 A-小s的签到题 无注释版 有注释版 B-行列改写 无注释版 有注释版 C-树上替身追赶游戏 无注释版 有注释版 A-小s的签到题 无注释版 #include<bits/stdc.h> using namespace std; struct f{char ch;int x; }a[110]; bool cmp(f p,f q){if(p.xq.x) return p…...