数据结构第九讲:二叉树
数据结构第九讲:二叉树
- 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.职位发布与管理…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...