数据结构第九讲:二叉树
数据结构第九讲:二叉树
- 1.实现链式结构二叉树
- 1.1二叉树的节点结构
- 1.2创建二叉树节点
- 1.3前中后序遍历
- 1.3.1前序遍历
- 1.3.2中序遍历
- 1.3.3后序遍历
- 1.3.4总结
- 1.4二叉树结点的个数
- 1.4.1错误示范
- 1.4.2实现方法
- 1.5二叉树叶子结点的个数
- 1.6二叉树第k层结点的个数
- 1.7二叉树的深度/高度
- 1.8二叉树查找值为x的结点
- 1.9二叉树的销毁
- 2.二叉树的层序遍历
- 2.1什么是层序遍历
- 2.2层序遍历的实现
- 2.2.1实现思路
- 2.2.2先创建一个队列
- 2.2.3代码的实现
- 3.判断是否为完全二叉树
- 3.1解题思路
- 3.2代码实现
这一讲我们要实现二叉树的链式结构,二叉树结构体中包含了数据、指向左孩子节点的指针和指向右孩子节点的指针,在这一讲中,我们将要体会的递归的暴力!!!
1.实现链式结构二叉树
1.1二叉树的节点结构
//二叉树的节点结构
typedef int BinaryTreeDataType;typedef struct BinaryTreeNode
{BinaryTreeDataType data;//保存数据struct BinaryTreeNode* left;//指向左孩子节点struct BinaryTreeNode* right;//指向右孩子节点
}BTNode;
1.2创建二叉树节点
也就是为二叉树创建节点,并将节点进行初始化
//创建二叉树节点
BTNode* BuyBTNode(int val)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("malloc fail!");return NULL;}newnode->data = val;newnode->left = newnode->right = NULL;return newnode;
}
1.3前中后序遍历
接下来我们要实现的是二叉树的遍历:
二叉树的遍历操作分为三种:前序遍历、中序遍历、后序遍历:

可以看出:这里区分的前中后其实就是根节点遍历的顺序
我们先总体看三种遍历的不同:

接下来我们来实现三种遍历,注意:三种遍历方法的代码实现非常简单,主要是思路的体会,三种方法都是使用的递归的思想:
1.3.1前序遍历
//前序遍历
//函数传入的是树的根节点
void PreOrder(BTNode* root)
{if (root == NULL){return;}//将节点的数据进行打印printf("%d ", root->data);PreOrder(root->left);PreOrder(root->right);
}

对于递归,return之后只是一个递归函数终止,然而形参的值不变,函数会继续向下执行,形成一个全新的递归函数:
1.3.2中序遍历
//中序遍历
void InOrder(BTNode* root)
{//也就是左根右进行遍历if (root == NULL){return;}//先对左边的数据进行读取,其实就是将左边的节点当成是根节点进行传入InOrder(root->left);//先打印根节点的值,然后再检查右节点是否为空printf("%d ", root->data);//当右节点不为空时,它会按照从上向下的顺序一直走到右节点的尽头//当然,当有右节点中存在左节点时,会先走左节点的循环,因为左节点的循环在上//而且会一下走到左节点的尽头,然后从下往上遍历左节点InOrder(root->right);
}
1.3.3后序遍历
//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){return;}//仍然是先走到最后一个左节点PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}
1.3.4总结

1.4二叉树结点的个数
1.4.1错误示范
根据我们之前所讲,那么我们应该会有一个初步的思路,我们先实现一下:
//二叉树结点个数
//先定义一个变量sz
int sz = 0;
int BTSize(BTNode* root)
{if (root == NULL){return 0;}//每循环一次,那么就将sz++一次++sz;BTSize(root->left);BTSize(root->right);return sz;
}
这时打印的结果也是非常beautiful啊,和预想的一样,但是,当我们这样时:
//二叉树结点个数
int size = BTSize(n1);
printf("%d ", size);//6
size = BTSize(n1);
printf("%d ", size);//12
我们就会惊奇地发现:结点的次数竟然会随着函数调用次数的增加而成倍地增长!原因就是使用了全局变量,全局变量在函数使用后不会销毁,那么我们就要进行更改了:
1.4.2实现方法
//二叉树结点个数
int BTSize(BTNode* root)
{if (root == NULL){return 0;}//返回左节点的个数和右节点的个数return 1 + BTSize(root->left) + BTSize(root->right);
}
1.5二叉树叶子结点的个数
//二叉树叶子结点个数
int BTLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}//这里的返回值也会参与加法运算,所以会呈现出累加的效果//也就是说,返回值会存储到BTLeafSize(root->left)或另一个函数中,然后再进入加法运算//返回左边的树的叶子节点个数 + 右边的树的叶子结点个数return BTLeafSize(root->left) + BTLeafSize(root->right);
}
对于递归,先搞清总体思路,如上边的:返回左边的树的叶子节点个数 + 右边的树的叶子结点个数,然后再想清楚结束条件,如:当左节点和右节点都为0时返回1,此时会发现递归已经写完了!!!
1.6二叉树第k层结点的个数
//二叉树第k层结点的个数
int BTLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}//返回条件:当k = 1时if (k == 1){return 1;}//返回左边的二叉树第k层的节点个数和右边二叉树第k层的结点个数return BTLevelKSize(root->left, k - 1) + BTLevelKSize(root->right, k - 1);
}
1.7二叉树的深度/高度
要想在递归的过程中对数据逐渐进行增大,必须要让return返回的值被接收,而且参与递归运算,这里是创建变量进行存储,还可以使用递归函数进行存储,如:1+BTDeapth(root->right)(这里是瞎写,仅代表一个格式)
//二叉树的深度/高度
int BTDepth(BTNode* root)
{if (root == NULL){return 0;}int leftdepth = BTDepth(root->left);int rightdepth = BTDepth(root->right);//要想在递归的过程中对数据逐渐进行增大,必须要让return返回的值被接收,而且参与递归运算//这里是创建变量进行存储//还可以使用递归函数进行存储,如:1+BTDeapth(root->right)(这里是瞎写,仅代表一个格式)return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
}

1.8二叉树查找值为x的结点
//二叉树查找值为x的结点
BTNode* BTFind(BTNode* root, BinaryTreeDataType x)
{if (root == NULL){return NULL;}//返回条件:当数据是我们想要的数据时,就进行返回if (root->data == x){return root;}BTNode* left = BTFind(root->left, x);if (left){//这里要十分注意的是://这里的return表示的是整个函数的返回,上面的return代表的是递归函数的返回//原因就在于使用了一个值来接受递归函数的返回值,使得递归函数结束递归了//如果这里不适用变量来接受的话,函数将会错误返回return left;}BTNode* right = BTFind(root->right, x);if (right){return right;}return NULL;
}
1.9二叉树的销毁
//二叉树的销毁
//因为销毁要改变指针的指针的指向,所以这里传的是二级指针
void BTDestory(BTNode** root)
{if (*root == NULL){return;}//这里的传参要注意:因为是二级指针接收,所以传入的应该是一级指针的地址//直接遍历所有结点,然后一一删除即可BTDestory(&(*root)->left);BTDestory(&(*root)->right);free(*root);*root = NULL;
}
2.二叉树的层序遍历
2.1什么是层序遍历
层序遍历就是按照层数,从左到右一次对数据进行遍历:

2.2层序遍历的实现
2.2.1实现思路
对于递归,它会一直执行下去,直到遇到结束条件,然而,这个算法中,并不支持递归的使用,因为我们想不出来什么结束条件能够成立,所以这时我们就想到了其它的方法:队列!!!

下面我们来看实现方法:
2.2.2先创建一个队列
恰巧我们刚刚学过了队列,所以我们完全可以将之前写的队列代码拿过来,但是要注意的是,之前我们所实现的队列中保存的值为int类型,但是现在因为插入的值为BTNode*类型,所以还要将类别进行更改:
//结点结构体
//尽管我们之前已经使用typedef将结构体的名字改变成了BTNode*
//这里仍然需要加上struct,否则编译器会识别不出来,万一是一个变量名呢对不对
typedef struct BTNode* QueueDataType;typedef struct QueueNode
{//和链表一样,也需要结点进行链接QueueDataType val;struct QueueNode* next;
}QueueNode;
2.2.3代码的实现
//二叉树层序遍历
void LevelOrder(BTNode* root)
{//先创建一个队列Queue q;//队列初始化Init(&q);//将二叉树链表入队列QueuePush(&q, root);//循环,当队列为空时结束循环,当队列不为空时进行循环while (!QueueEmpty(&q)){//将一个结点出队列BTNode* ret = QueueFront(&q);printf("%d ", ret->data);QueuePop(&q);//如果有左右结点的话,按顺序入队列if (ret->left){QueuePush(&q, ret->left);}if (ret->right){QueuePush(&q, ret->right);}}Destory(&q);
}
3.判断是否为完全二叉树
3.1解题思路
这一道题目仍然是应用队列的知识:

3.2代码实现
//判断是否为完全二叉树
bool BinaryTreeComplete(BTNode* n1)
{//先创建一个队列Queue q;Init(&q);//先将二叉树中的数据全部插入到队列中//先将对头元素插入到队列中QueuePush(&q, n1);while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);if (top == NULL){break;}//如果top不为空,将top的左孩子结点和右孩子结点入队,这样就保障了NULL结点的入队QueuePush(&q, top->left);QueuePush(&q, top->right);}//入队完成之后,检查队列中的数据,不能够存在一个NULL数据while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);//如果存在不为空的数据,返回falseif (top){Destory(&q);return false;}}Destory(&q);return true;
}
相关文章:
数据结构第九讲:二叉树
数据结构第九讲:二叉树 1.实现链式结构二叉树1.1二叉树的节点结构1.2创建二叉树节点1.3前中后序遍历1.3.1前序遍历1.3.2中序遍历1.3.3后序遍历1.3.4总结 1.4二叉树结点的个数1.4.1错误示范1.4.2实现方法 1.5二叉树叶子结点的个数1.6二叉树第k层结点的个数1.7二叉树的…...
英伟达推出B200A瞄准OEM客群,预估2025年高端GPU出货量年增55%
市场近日传出NVIDIA(英伟达)取消B100并转为B200A,据TrendForce集邦咨询了解,NVIDIA仍计划在2024年下半年推出B100及B200,供应CSPs(云端服务业者)客户,并另外规划降规版B200A给其他企…...
Codeforces Round 962 (Div. 3)-补题
A. Legs 二分答案,最后取左端点的值,保险起见,还是再验算一次 bool check(int x){int an/4;if(a*4(x-a)*2>n) return true;return false; }void solve(){cin>>n;int l0,rn;while(l1<r){int midlr>>1;if(check(mid)) rmid…...
pandas的文本与序列化
文章目录 1.pandas的文本与序列化 result_data pd.DataFrame(json_data_list)with open(jsonl_file_path, w, encodingutf-8) as jsonl_file:result_data.to_json(orientrecords, linesTrue, force_asciiFalse, path_or_bufjsonl_file)数据不换行 df.at[i, column_name_transc…...
在企业级环境中部署Java程序:Docker命令实用指南
在企业级环境中部署Java程序:Docker命令实用指南 引言 在企业级开发中,Java应用程序的部署往往需要考虑效率、安全性和可移植性。Docker作为一个流行的容器化平台,提供了一种简便、一致且可移植的方式来部署Java应用。以下是一些常用的Dock…...
LabVIEW远程开发
LabVIEW远程开发是指在不同地点的开发者通过网络协同工作,共同开发、调试和维护基于LabVIEW的应用程序。这种开发模式适用于分布式团队、远程办公和全球化项目合作,能够有效利用不同地区的人才和资源。以下是LabVIEW远程开发的详细介绍: 1. 远…...
工作随记:我在OL8.8部署oracle rac遇到的问题
文章目录 一、安装篇问题1:[INS-08101] Unexpected error while executing the action at state:supportedosCheck问题1解决办法:问题2:[INS-06003] Failed to setup passwordless SSH connectivity with thefollowing nodeis): [xxxx1, xxxx…...
C++:vector容器
概览 std::vector是C标准模板库(STL)中的一种动态数组容器。它提供了一种类似于数组的数据结构,但是具有动态大小和更安全的内存管理。 定义和基本特性 std::vector是C标准库中的一 个序列容器,它代表了能够动态改变大小的数组。与普通数组一样&#x…...
深入理解 AWS CodePipeline
AWS CodePipeline 是一种持续交付和持续集成(CI/CD)服务,用于自动化软件发布过程。它通过创建流水线来帮助你自动构建、测试和部署应用程序。以下是对 AWS CodePipeline 的深入理解,包括其工作原理、组件、功能和使用场景: 1. AWS CodePipeline 的基本概念 持续集成和持续…...
Qt:自定义钟表组件
使用QWidget绘制两种钟表组件,效果如下: 源码下载链接:GitHub - DengYong1988/Clock-Widget: Qt 自定义钟表组件 https://download.csdn.net/download/ouyangxiaozi/89616407 主要代码如下: ClockWgt.h #ifndef CLOCKWGT_H #d…...
前端性能优化-web资源加载优先级
前言 资源加载优先级是指在页面渲染的过程中,浏览器决定加载哪些资源并优先加载它们的一种机制。正确配置资源加载的优先级可以显著改善页面加载性能,确保关键资源优先加载,提高用户感知的加载速度。 Web 资源加载方式 同步加载 同步加载…...
Docker-数据卷指令
数据卷挂载修改内容...
Elasticsearch VS Typesense! Elasticsearch未来会被其它搜索引擎取代吗?
近期网上流行一批新的搜索引擎,动不动就大言不惭,要跟龙头老大Elasticsearch比,想把Elasticsearch击败。 1. Typesense 太猖狂了,对Elasticsearch极为不敬 如近期炒作很猖狂的Typesense开源搜索引擎,一出来就急着挑战…...
usb摄像头 按钮 静止按钮
usb摄像头 按钮 静止按钮 来分析一个UVC的摄像头的枚举信息 UVC学习:UVC中断端点介绍 https://www.eet-china.com/mp/a269529.html 输入命令lsusb -d 0c45:62f1 -v https://www.miaokee.com/705548.html >Video Class-Specific VS Video Input Header Descrip…...
SAP MM学习笔记 - 豆知识03 - 安全在库和最小安全在库,扩张物料的保管场所的几种方法,定义生产订单的默认入库保管场所,受注票中设定禁止贩卖某个物料
上一章讲了一些MM模块的豆知识。 - MR21 修改物料原价 - MM02 修改基本数量单位/评价Class - MMAM 修改物料类型/评价Class SAP MM学习笔记 - 豆知识02 - MR21 修改物料原价,MM02 修改基本数量单位/评价Class,MMAM 修改物料类型/评价Class-CSDN博客 …...
激光导航AGV叉车那么多,究竟该怎么选?一篇文章讲明白~
AGV叉车 随着经济的快速发展,大部分企业的物料搬运开始脱离人工劳作,取而代之的是以叉车为主的机械化搬运。AGV叉车是工业搬运车辆,是指对成件托盘货物进行装卸、堆垛和短距离运输作业的各种轮式搬运车辆,主要应用于货场、工厂车间…...
redis面试(七)初识lua加锁脚本
redisson redisson如何来进行redis分布式锁实现的源码,基于redis实现各种各样的分布式锁的原理 https://redisson.org/ 这是官网 https://github.com/redisson/redisson/wiki/Table-of-Content 这是官方文档 开始 demo 建一个普通的工程在pom.xml里引入依赖 <…...
企元数智百年营销史的精粹:借鉴历史创造未来商机
随着时代的发展和科技的进步,传统营销方式正在经历前所未有的颠覆和改变。在这个数字化时代,企业需要不断创新,同时借鉴百年营销史的精粹,汲取历史经验,创造未来商机。而"企元数智"作为现代营销的代表&#…...
Java @SpringBootTest注解用法
SpringBootTest 是 Spring Framework 中的一个注解,用于指示 Spring Boot 应用程序的测试类。当你在测试类上使用 SpringBootTest 注解时,Spring Boot 会启动一个 Spring 应用程序上下文,并且加载应用程序的 application.properties 或 appli…...
构建智能招聘平台:人才招聘系统源码开发指南
本篇文章,小编将详细探讨如何基于人才招聘系统源码开发一个智能招聘平台,为企业的人才战略提供支持。 一、智能招聘平台的核心功能 智能招聘平台的核心在于提高招聘效率和匹配度,这需要集成多个关键功能模块: 1.职位发布与管理…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...

