【新春不断更】数据结构与算法之美:二叉树
Hello大家好,我是但凡!很高兴我们又见面啦!
眨眼间已经到了2024年的最后一天,在这里我要首先感谢过去一年陪我奋斗的每一位伙伴,是你们给予我不断前行的动力。银蛇携福至,万象启新程。蛇年新春之际,愿你们万事顺遂,岁月皆安,新的一年所想皆如愿,所行皆坦途 。
好了,给生活添点passion,开始今天的编程之路!
我的博客:<但凡.
我的专栏:《编程之路》、《数据结构与算法之美》、《题海拾贝》
欢迎点赞,关注!
目录
1、 二叉树的动态模拟
1.1新建节点
1.2建树
1.3计算树的节点个数
1.3.1方法一
1.3.2方法二
1.4计算树的叶子节点个数
1.5 计算树的第K层节点个数
1.6 树的深度
1.7查找节点
1.8 遍历
1.8.1前序遍历
1.8.2中序遍历
1.8.3后序遍历
1.8.4层序遍历(广度优先遍历)
1.9判断二叉树是否为完全二叉树
1.10销毁二叉树
2、二叉树的静态模拟实现
1、 二叉树的动态模拟
我们用链式结构实现二叉树。一般链式结构实现二叉树,我们的结构中包含三个元素,一是该节点的数据,二是左子树节点,三是右子树节点
typedef char BTtype;
typedef struct BinaryTreeNode
{BTtype x;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTnode;
1.1新建节点
BTnode* BTbuybode(char a)//你这声明和定义都不是一个意思好的
{BTnode* p = (BTnode*)malloc(sizeof(BTnode));if (p == NULL){perror("malloc error!");exit(1);}p->left = NULL;p->right = NULL;p->x = a;return p;
}
1.2建树
需要注意的是,我们的树是需要手动去建的,所以我在这只是给大家一个示例:
//建树
BTnode* nodeA=BTbuybode('A');
BTnode* nodeB = BTbuybode('B');
BTnode* nodeC = BTbuybode('C');
BTnode* nodeD = BTbuybode('D');
BTnode* nodeE = BTbuybode('E');
nodeA->left = nodeB;
nodeA->right = nodeC;
nodeB->left = nodeD;
nodeC->left = nodeE;
BTnode* root = nodeA;
建的树是这样的:
那么接下来我们就实现以下和树相关的操作。准备好迎接递归的极致暴力美学!
1.3计算树的节点个数
1.3.1方法一
方法一就是咱们把size作为一个函数的形参,然后把这个树遍历一遍,每遍历一个节点就size(节点个数)加一。但需要注意的是,我们需要传入size的地址才能改变size的值。
void BinaryTreeSize(BTnode* root,int* size)
{if (root == NULL){return;}(*size)++;BinaryTreeSize(root->left,size);BinaryTreeSize(root->right, size);
}
1.3.2方法二
方法二是纯递归: 节点个数=左子树节点个数+右子树节点个数,所以我们以此为基础递归就可以了。
int BinaryTreeSize(BTnode* root)
{//节点个数=左子树节点个数+右子树节点个数//递归出口if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
1.4计算树的叶子节点个数
树的叶子结点就是没有左右子树的节点,所以咱们得设置两个递归出口。
int BinaryTreeLeafSize(BTnode* root)
{//递归出口if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
1.5 计算树的第K层节点个数
当咱们K减成1的时候,就说明到达了第K层。
int BinaryTreeLevelKSize(BTnode* root, int k)
{//递归出口if(k==1){if (root == NULL){return 0;}else{return 1;}}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
1.6 树的深度
注意我是拿C++写的,用了自带的函数max,如果使用C语言写的话max函数得自己写,或者用一个问号表达式来实现类似效果。
int BinaryTreeDeep(BTnode* root)
{//计算树的深度if (root == NULL){return 0;}return 1 + max(BinaryTreeDeep(root->left), BinaryTreeDeep(root->right));
}
1.7查找节点
BTnode* BinaryTreeFind(BTnode* root, BTtype x)
{//递归出口if (root == NULL){return 0;}if (root->x == x){return root;}BTnode* left = BinaryTreeFind(root->left, x);if(left){return root;}BTnode* right = BinaryTreeFind(root->right, x);if (right){return root;}return NULL;
}
1.8 遍历
1.8.1前序遍历
前序遍历就是先遍历头节点,然后遍历左子树,最后遍历右子树。我们可以把它拆开来想,我们左子树依然用先遍历头,再遍历左子树,最后遍历右子树的方式来遍历,左子树的左子树依然如此......
void BinaryTreePrevOrder(BTnode* root)
{//头 左 右//递归出口if (root == NULL){cout << "NULL" << " ";return;}cout << root->x << " ";BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}
1.8.2中序遍历
void BinaryTreeInOrder(BTnode* root)
{//左 头 右//递归出口if (root == NULL){cout << "NULL" << " ";return;}BinaryTreeInOrder(root->left); //注意别调用错了,调用中序的cout << root->x << " ";BinaryTreeInOrder(root->right);
}
1.8.3后序遍历
void BinaryTreePostOrder(BTnode* root)
{//递归出口if (root == NULL){cout << "NULL" << " ";return;}BinaryTreePostOrder(root->left);BinaryTreePostOrder(root->right);cout << root->x << " ";
}
我们可以发现,这三个遍历的不同就是打印根节点的位置发生了变化 。我们以上三个遍历都属于深度优先遍历。
1.8.4层序遍历(广度优先遍历)
void BinaryTreeLevelOrder(BTnode* root)
{queue<BTnode*> q;//创建队列q.push(root);while (!q.empty()){BTnode* tmp = q.front();q.pop();cout << tmp->x << " ";//左右子树入队列if (tmp->left){q.push(tmp->left);}if (tmp->right){q.push(tmp->right);}}
}
这个层序遍历我用了c++自带的队列,如果用C语言写的话我们可以把模拟实现的队列文件导入。我之前发过队列的模拟实现,给大家放在这里: 数据结构与算法之美:队列-CSDN博客
1.9判断二叉树是否为完全二叉树
bool BinaryTreeComplete(BTnode* root)
{queue<BTnode*> q;//创建队列q.push(root);while (!q.empty()){BTnode* tmp = q.front();q.pop();if (tmp==NULL){break;}//左右子树入队列q.push(tmp->left);q.push(tmp->right);}//现在队列中如果还有不为空的节点,就说明不是完全二叉树 while (!q.empty()){BTnode* tmp = q.front();q.pop();if (tmp){return false;}}return true;
}
1.10销毁二叉树
void BinaryTreeDestory(BTnode** root)
{//这里因为咱们要改变根节点,应该传入的是根节点的地址,所以得拿二级指针接收//递归出口if ((*root) == NULL){return;}//自叶向根方向的释放//如果先释放的话,就找不到叶子节点了BinaryTreeDestory(&((*root)->left));BinaryTreeDestory(&((*root)->right));free(*root);*root = NULL;
}
所有测试代码:
#include"BinaryTree.h"
void test()
{//建树BTnode* nodeA=BTbuybode('A');BTnode* nodeB = BTbuybode('B');BTnode* nodeC = BTbuybode('C');BTnode* nodeD = BTbuybode('D');BTnode* nodeE = BTbuybode('E');nodeA->left = nodeB;nodeA->right = nodeC;nodeB->left = nodeD;nodeC->left = nodeE;BTnode* root = nodeA;//计算节点个数int size = 0;BinaryTreeSize(root, &size);cout <<"节点个数:"<< size<< endl;//计算叶子节点个数cout << "叶子节点个数:" << BinaryTreeLeafSize(root) << endl;//计算第三层节点个数cout << "第三层节点个数:" << BinaryTreeLevelKSize(root, 3) << endl;//计算二叉树深度cout<<"二叉树深度:"<< BinaryTreeDeep(root) << endl;//查找值为E的节点BTnode* node = BinaryTreeFind(root, 'E');if (node)//已找到{cout << "已找到该节点" << endl;}else{cout << "未找到该节点" << endl;}//查找值为G的节点BTnode* node1 = BinaryTreeFind(root, 'G');if (node1)//已找到{cout << "已找到该节点" << endl;}else{cout << "未找到该节点" << endl;}//前序遍历BinaryTreePrevOrder(root);cout << endl;// 二叉树中序遍历BinaryTreeInOrder(root);cout << endl;//后序遍历BinaryTreePostOrder(root);cout << endl;//广度优先遍历BinaryTreeLevelOrder(root);cout << endl;//是否为完全二叉树if (BinaryTreeComplete(root)){cout << "是完全二叉树" << endl;}else{cout << "不是完全二叉树" << endl;}//二叉树的销毁BinaryTreeDestory(&root);
}
int main()
{test();return 0;
}
测试结果:
2、二叉树的静态模拟实现
之前我们介绍堆的时候建堆用的是vector数组,其实静态建树一共有两个方式。一个是vector数组,一个是链式前向星。二叉树当然也可以用这两个方法建,但是呢对于二叉树来说有一个特殊的方法来建树。
我们创建两个足够大数组,这两个数组分别记录着下标为k的左右节点的值。其中这个k是当前这个节点的值。咱们建树的时候都默认根节点的值为1.
#include<iostream>
#include<queue>
using namespace std;
const int N = 1e4 + 10;
int l[N], r[N];
queue<int> q;
void bfs()
{q.push(1);while (q.size()){int v = q.front();cout << v << " ";q.pop();//由于二叉树的存储找不到当前节点的父节点(也就是不能向上查找)//所以不需要bool数组标记if(l[v]){q.push(l[v]);}if(r[v]){q.push(r[v]);}}
}
void dfs1(int v)
{cout <<v<<" ";if (l[v]) dfs1(l[v]);if (r[v]) dfs1(r[v]);
}
void dfs2(int v)
{if (l[v]) dfs2(l[v]);cout << v << " ";if (r[v]) dfs2(r[v]);
}
void dfs3(int v)
{if (l[v]) dfs3(l[v]);if (r[v]) dfs3(r[v]);cout << v << " ";
}
int main()
{//建二叉树int n;cin >> n;//节点个数cin >> l[1] >> r[1];//l[],r[]分别存储当前节点的左右孩子,0代表没有该孩子 for(int i=2;i<=n;i++)//循环n-1次{cin >> l[i] >> r[i];}//二叉树新增的深度优先遍历方式//前序遍历dfs1(1);cout << endl;//中序遍历dfs2(1);cout << endl;//后序遍历dfs3(1);cout << endl;//宽度优先遍历bfs();
}
我们可以发现他的各种遍历的思路是和咱们动态实现一样的。
二叉树练习题:题海拾贝:二叉树的模拟题-CSDN博客
题海拾贝:[USACO3.4] 美国血统AmericanHeritage(求先序排列问题)-CSDN博客
题海拾贝:[JLOI2009] 二叉树问题-CSDN博客
好了,今天的内容就分享到这,我们下期再见!
相关文章:
【新春不断更】数据结构与算法之美:二叉树
Hello大家好,我是但凡!很高兴我们又见面啦! 眨眼间已经到了2024年的最后一天,在这里我要首先感谢过去一年陪我奋斗的每一位伙伴,是你们给予我不断前行的动力。银蛇携福至,万象启新程。蛇年新春之际…...
网站结构优化:加速搜索引擎收录的关键
本文来自:百万收录网 原文链接:https://www.baiwanshoulu.com/9.html 网站结构优化对于加速搜索引擎收录至关重要。以下是一些关键策略,旨在通过优化网站结构来提高搜索引擎的抓取效率和收录速度: 一、合理规划网站架构 采用扁…...
Effective Objective-C 2.0 读书笔记—— objc_msgSend
Effective Objective-C 2.0 读书笔记—— objc_msgSend 文章目录 Effective Objective-C 2.0 读书笔记—— objc_msgSend引入——静态绑定和动态绑定OC之中动态绑定的实现方法签名方法列表 其他方法objc_msgSend_stretobjc_msgSend_fpretobjc_msgSendSuper 尾调用优化总结参考文…...
[MySQL]事务的隔离级别原理与底层实现
目录 1.为什么要有隔离性 2.事务的隔离级别 读未提交 读提交 可重复读 串行化 3.演示事务隔离级别的操作 查看与设置事务的隔离级别 演示读提交操作 演示可重复读操作 1.为什么要有隔离性 在真正的业务场景下,MySQL服务在同一时间一定会有大量的客户端进程…...
项目升级Sass版本或升级Element Plus版本遇到的问题
项目升级Sass版本或升级Element Plus版本遇到的问题 如果项目有需求需要用到高版本的Element Plus组件,则需要升级相对应的sass版本,Element 文档中有提示,2.8.5及以后得版本,sass最低支持的版本为1.79.0,所升级sass、…...
C++中,存储两个相同类型的数据,数据结构
在C中,存储两个相同类型的数据,可以使用多种数据结构。这里有几种常见且合适的选择: 简单的变量: 最直接的方式就是使用两个独立的变量。这种方法简单直观,但不够结构化。 cpp int a 5; int b 10; std::pair&#x…...
python实战(十五)——中文手写体数字图像CNN分类
一、任务背景 本次python实战,我们使用来自Kaggle的数据集《Chinese MNIST》进行CNN分类建模,不同于经典的MNIST数据集,我们这次使用的数据集是汉字手写体数字。除了常规的汉字“零”到“九”之外还多了“十”、“百”、“千”、“万”、“亿…...
[论文阅读] (37)CCS21 DeepAID:基于深度学习的异常检测(解释)
祝大家新春快乐,蛇年吉祥! 《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座,并分享给大家,希望您喜欢。由于作者的英文水平和学术能力不高,需要不断提升,所以还请大家批评指正࿰…...
Linux - 进程间通信(2)
目录 2、进程池 1)理解进程池 2)进程池的实现 整体框架: a. 加载任务 b. 先描述,再组织 I. 先描述 II. 再组织 c. 创建信道和子进程 d. 通过channel控制子进程 e. 回收管道和子进程 问题1: 解答1ÿ…...
Kafka 消费端反复 Rebalance: `Attempt to heartbeat failed since group is rebalancing`
文章目录 Kafka 消费端反复 Rebalance: Attempt to heartbeat failed since group is rebalancing1. Rebalance 过程概述2. 错误原因分析2.1 消费者组频繁加入或退出2.1.1 消费者故障导致频繁重启2.1.2. 消费者加入和退出导致的 Rebalance2.1.3 消费者心跳超时导致的 Rebalance…...
SpringBoot+Electron教务管理系统 附带详细运行指导视频
文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码1.查询课程表代码2.保存学生信息代码3.用户登录代码 一、项目演示 项目演示地址: 视频地址 二、项目介绍 项目描述:这是一个基于SpringBootElectron框架开发的教务管理系统。首先ÿ…...
操作系统(Linux Kernel 0.11Linux Kernel 0.12)解读整理——内核初始化(main init)之控制台工作
前言 在 Linux 内核中,字符设备主要包括控制终端设备和串行终端设备,对这些设备的输入输出涉及控制台驱动程序,这包括键盘中断驱动程序 keyboard.S 和控制台显示驱动程序 console.c,还有终端驱动程序与上层程序之间的接口部分。 终端驱动程序…...
Autogen_core: Message and Communication
目录 完整代码代码解释1. 消息的数据类:2. 创建代理人(MyAgent):3. 创建和运行代理人的运行时环境:4. 根据发送者路由消息的代理(RoutedBySenderAgent):5. 创建和运行带路由的代理&a…...
ComfyUI工作流教程、软件使用、开发指导、模型下载
在人工智能和设计技术迅速发展的今天,AI赋能的工作流已成为创意设计与生产的重要工具。无论是图片处理、服装试穿,还是室内设计与3D建模,这些智能化的解决方案极大地提高了效率和创作质量。 为了帮助设计师、开发者以及AI技术爱好者更好地利用这些工具,我们整理了一份详尽…...
零基础Vue学习1——Vue学习前环境准备
目录 环境准备 创建Vue项目 项目目录说明 后续开发过程中常用命令 环境准备 安装开发工具:vscode、webstorm、idea都可以安装node:V22以上版本即可安装pnpm 不知道怎么安装的可以私信我教你方法 创建Vue项目 本地新建一个文件夹,之后在文件夹下打开…...
定西市建筑房屋轮廓数据shp格式gis无偏移坐标(字段有高度和楼层)内容测评
定西市建筑房屋轮廓数据是GIS(Geographic Information System,地理信息系统)领域的重要资源,用于城市规划、土地管理、环境保护等多个方面。这份2022年的数据集采用shp(Shapefile)格式,这是一种…...
汉语向编程指南
汉语向编程指南 一、引言王阳明代数与流形学习理论慢道缓行理性人类型指标系统为己之学与意气实体过程晏殊几何学半可分离相如矩阵与生成气质邻域镶嵌气度曲面细分生成气质邻域镶嵌气度曲面细分社会科学概论琴生生物机械科技工业研究所软凝聚态物理开发工具包琴生生物机械 报告…...
Writing an Efficient Vulkan Renderer
本文出自GPU Zen 2。 Vulkan 是一个新的显式跨平台图形 API。它引入了许多新概念,即使是经验丰富的图形程序员也可能不熟悉。Vulkan 的主要目标是性能——然而,获得良好的性能需要深入了解这些概念及其高效应用方法,以及特定驱动程序实现的实…...
AI常见的算法
人工智能(AI)中常见的算法分为多个领域,如机器学习、深度学习、强化学习、自然语言处理和计算机视觉等。以下是一些常见的算法及其用途: 1. 机器学习 (Machine Learning) 监督学习 (Supervised Learning) 线性回归 (Linear Regr…...
LibreChat
文章目录 一、关于 LibreChat✨特点 二、使用LibreChat🪶多合一AI对话 一、关于 LibreChat LibreChat 是增强的ChatGPT克隆:Features Agents, Anthropic, AWS, OpenAI, Assistants API, Azure, Groq, o1, GPT-4o, Mistral, OpenRouter, Vertex AI, Gemi…...
PHP PhantomJS 安装与使用指南
PHP PhantomJS 安装与使用指南 【免费下载链接】php-phantomjs Execute PhantomJS commands through PHP 项目地址: https://gitcode.com/gh_mirrors/ph/php-phantomjs 1. 项目目录结构及介绍 在安装jonnnnyw/php-phantomjs库后,您将得到一个基本的目录结构…...
LangFlow零代码AI应用搭建:5分钟可视化构建智能问答机器人
LangFlow零代码AI应用搭建:5分钟可视化构建智能问答机器人 1. LangFlow简介:零代码AI应用构建利器 LangFlow是一款革命性的可视化AI应用构建工具,它让不懂编程的用户也能轻松搭建智能问答机器人。想象一下,你只需要像搭积木一样…...
使用圣女司幼幽-造相Z-Turbo为MATLAB科学计算可视化生成示意图
使用圣女司幼幽-造相Z-Turbo为MATLAB科学计算可视化生成示意图 如果你用MATLAB做科研或者工程计算,肯定遇到过这样的烦恼:辛辛苦苦算出来的数据,最后要画图放进论文或者报告里时,总觉得那些图表有点“干巴巴”的,不够…...
Qwen3-ASR-0.6B效果展示:金融客服录音(专业术语+缩略语)识别术语表匹配
Qwen3-ASR-0.6B效果展示:金融客服录音(专业术语缩略语)识别术语表匹配 金融客服电话录音里,客户和坐席的对话常常像在说“天书”。一会儿是“LPR”,一会儿是“T0”,还有各种产品代码和内部术语。把这些录音…...
Kimi-K2-W8A8量化版:推理精度反超官方!
Kimi-K2-W8A8量化版:推理精度反超官方! 【免费下载链接】KIMI-k2-Thinking-W8A8-QuaRot 项目地址: https://ai.gitcode.com/Eco-Tech/KIMI-k2-Thinking-W8A8-QuaRot 导语:国内大模型量化技术再获突破——Kimi-K2-Thinking模型的W8A8量…...
Octomap在二维导航地图转换中的常见问题与优化策略
1. Octomap二维地图转换的核心挑战 第一次接触Octomap进行三维到二维地图转换时,我被它强大的空间建模能力吸引,但实际操作中踩了不少坑。最典型的就是发现生成的二维地图要么全是噪点,要么和实际环境对不上。后来才明白,这背后涉…...
3个关键场景与4步操作:深入解析RevokeMsgPatcher防撤回工具的技术实现与应用实践
3个关键场景与4步操作:深入解析RevokeMsgPatcher防撤回工具的技术实现与应用实践 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁(我已经看到了,撤回也没用了) 项目…...
3步打造纯净音乐体验:铜钟音乐开源播放器技术解析
3步打造纯净音乐体验:铜钟音乐开源播放器技术解析 【免费下载链接】tonzhon-music 铜钟 (Tonzhon.com): 免费听歌; 没有直播, 社交, 广告, 干扰; 简洁纯粹, 资源丰富, 体验独特!(密码重置功能已回归) 项目地址: https://gitcode.com/GitHub_Trending/t…...
从卡顿到流畅:Win11Debloat开源工具3步解决Windows系统优化难题
从卡顿到流畅:Win11Debloat开源工具3步解决Windows系统优化难题 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本,用于从Windows中移除预装的无用软件,禁用遥测,从Windows搜索中移除Bing,以及执行各种其他更改…...
MySQL源码编译部署主从及MHA高可用集群实战
一.Mysql的源码编译1.下载安装包wget https://downloads.mysql.com/archives/get/p/23/file/mysql-boost-8.3.0.tar.gz2.源码编译# 安装编译依赖的软件包,包括C/C编译器(如gcc/gcc-c)、构建工具(如cmake, git, bison)和开发库(如openssl-devel, ncurses-devel) [roo…...

