链式结构二叉树(递归暴力美学)


文章目录
- 1. 链式结构二叉树
- 1.1 二叉树创建
- 2. 前中后序遍历
- 2.1 遍历规则
- 2.2 代码实现
- 图文理解
- 3. 结点个数以及高度等
- 二叉树结点个数
- 正确做法:
- 4. 层序遍历
- 5. 判断是否完全二叉树
1. 链式结构二叉树
完成了顺序结构二叉树的代码实现,可以知道其底层结构是类似顺序表的结构;
因此,链式结构的二叉树类似于链表结构。
二叉树的结构一般由指数据域和左右指针域这三个域组成。
typedef char BTDataType;typedef struct BinaryTreeNode
{BTDataType data; //当前结点数据域struct BinaryTreeNode* left; //指向当前结点左孩⼦struct BinaryTreeNode* right; //指向当前结点右孩⼦
}BTNode;
1.1 二叉树创建
由于二叉树的代码实现过于复杂,因此这里采用手动创建一棵链式二叉树方便后续思路的代码实现。
单独封装一个函数可用来申请结点(和链表一样);
手动创建多个结点,并让其形成一棵二叉树。
BTNode* BuyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail!");exit(1);}//malloc成功node->data = x;node->left = node->right = NULL;return node;
}
//手动构造一颗二叉树
BTNode* CreaterTree()
{BTNode* newnodeA = BuyNode('A');BTNode* newnodeB = BuyNode('B');BTNode* newnodeC = BuyNode('C');BTNode* newnodeD = BuyNode('D');BTNode* newnodeE = BuyNode('E');BTNode* newnodeF = BuyNode('F');newnodeA->left = newnodeB;newnodeA->right = newnodeC;newnodeB->left = newnodeD;newnodeC->left = newnodeE;newnodeC->right = newnodeF;return newnodeA;
}

2. 前中后序遍历
既然是二叉树,那必然也离不开遍历。因此,我们有多种遍历方式。
2.1 遍历规则
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
- 前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发生在遍历其左右子树之前
访问顺序为:根结点、左子树、右子树(简称:根、左、右) - 中序遍历(Inorder Traversal):访问根结点的操作发生在遍历其左右子树之中(间)
访问顺序为:左子树、根结点、右子树(简称:左、根、右) - 后序遍历(Postorder Traversal):访问根结点的操作发生在遍历其左右子树之后
访问顺序为:左子树、右子树、根结点(简称:左、右、根)
以前序遍历为例如下图所示:
遵循前序遍历的规则:
1 先是访问A结点,然后进入左子树;
2. 在左子树中访问B根结点,进入B结点的左子树;
3. 在左子树中访问D根结点,而D中没有左子树和右子树,返回访问B的右子树;
4. B的右子树不存在,返回访问A的右子树;
5. 在右子树中访问C根结点,进入C结点的左子树;
6. 在左子树中访问E根结点,而E中没有左子树和右子树,返回访问C的右子树;
7. 在右子树中访问F根结点,F中没有左子树和右子树,遍历结束。
因此,前序遍历出来的数据是: A B D NULL NULL NULL C E NULL NULL F NULL NULL (NULL表示空)。


2.2 代码实现
为了方便理解,建议看完此二篇。
函数栈帧的创建和销毁.
函数递归的理解 <-- 详见此文
//前序遍历 --- 根左右
void PreOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c ", root->data);PreOrder(root->left);PreOrder(root->right);
}
图文理解
以上文的二叉树,前序遍历为例:

//中序遍历 --- 左根右
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}
//后序遍历 --- 左右根
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);
}
3. 结点个数以及高度等
二叉树结点个数
错误示例1:
int BinaryTreeSize(BTNode* root)
{static int size = 0;if (root == NULL){return 0;}++size;BinaryTreeSize(root->left);BinaryTreeSize(root->right);return size;
}
我们开始的想法是通过调用该函数,在里面通过变量size计数,但每次递推调用函数的时候size又会重新置为0,因此我们又考虑用static修饰size延长其生命周期,使得每次递推的时候都能使size保存上一个函数调用的值,最后返回size的值得到该二叉树的结点个数。
但是,这明显存在一个错误,如果我们再次计算这个二叉树结点个数会出现什么情况?

我们打断点调试会发现size的值是从6开始的,这是为什么呢?
很明显,size被static修饰延长了生命周期,使得该变量不再是因为栈空间的结束而销毁。因此,该做法是不可取的。

错误示例2:
void BinaryTreeSize(BTNode* root, int* psize)
{if (root == NULL){return 0;}++(*psize);BinaryTreeSize(root->left,psize);BinaryTreeSize(root->right,psize);
}
既然我们不能从函数内部计数,那么我们是否能在函数外部定义指针变量size(要使得形参的改变能影响实参)来计数呢?
虽然解决了生命周期被延长的做法,但下次调用函数依旧会出现问题,还是因为size没有从0开始(需要手动置为0)。
正确做法:
// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
深刻理解了递归和栈帧空间的创建销毁思想后,一棵二叉树是由根结点、左子树和右子树组成的,那么计算结点个数自然而然就是 1 + 左子树 + 右子树。
4. 层序遍历
建议看完此篇。
栈和队列 <-- 详见此文
层序遍历:从所在二叉树的根结点出发,先访问第⼀层的树根结点,然后从左到右访问第2
层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程。
实现层序遍历需要额外借助数据结构:队列
- 将二叉树的根节点传给队列(队列可根据根节点找到左子树和右子树);
- 取队头元素并打印,同时删除队头元素;
- 若存在左孩子或右孩子,依次入队列;
- 继续取队头元素并打印,删除队头元素同时将队头的左孩子和右孩子入队列(存在情况下);
- 如此循环下去,直到队列为空完成了层序遍历。

// 层序遍历
void LevelOrder(BTNode* root)
{//借助数据结构--队列Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){//取队头,出队头BTNode* top = QueueFront(&q);QueuePop(&q);printf("%c ", top->data);//队头左右孩子入队列(不为空)if (top->left){QueuePush(&q, top->left);}if (top->right){QueuePush(&q, top->right);}}QueueDestroy(&q);
}
5. 判断是否完全二叉树
非完全二叉树:

完全二叉树:

// 判断⼆叉树是否是完全⼆叉树
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);if (top == NULL){break;}//把不为空的队头节点的左右孩子入队列QueuePush(&q, top->left);QueuePush(&q, top->right);}//队列不一定为空,继续取队头出队头while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}


相关文章:
链式结构二叉树(递归暴力美学)
文章目录 1. 链式结构二叉树1.1 二叉树创建 2. 前中后序遍历2.1 遍历规则2.2 代码实现图文理解 3. 结点个数以及高度等二叉树结点个数正确做法: 4. 层序遍历5. 判断是否完全二叉树 1. 链式结构二叉树 完成了顺序结构二叉树的代码实现,可以知道其底层结构…...
使用类别数据编码进行连续变量的特征提取
在数据科学和机器学习中,数据预处理是模型构建的重要步骤。对于处理结构化数据时,特别是包含类别型数据的场景,类别数据编码是必不可少的步骤。类别数据通常以文字、标签等形式出现,但大多数机器学习算法只能处理数值型数据,因此需要将类别数据转化为数值格式。编码方式的…...
PCL 最小包围圆(二维)
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 最小包围圆是指能够包含给定点集中所有点的最小圆。这个算法通常用于计算几何、计算机图形学、机器学习等领域。以下是该算法的基本原理和实现流程: 1. 初始化:将点集中的所有点加入待处理列表。 2. 查找最远点:…...
技术文档管理最佳实践:高效、专业、可持续
文章目录 技术文档管理最佳实践:高效、专业、可持续1. 技术文档的核心价值1.1 降低知识流失风险1.2 提升开发效率1.3 增强团队协作1.4 规范技术资产管理 2. 技术文档分类与规范2.1 代码相关文档2.2 过程与运维文档2.3 知识与培训文档 3. 工具选型:自动化…...
56. Uboot移植实验
一、NXP官方Uboot编译与测试 1、将NXP提供的uboot拷贝到ubuntu中。 一个开发板也好运行uboot,DDR或者叫DRAM,串口,SD、EMMC、NAND。板子能工作。 测似结果: 1、uboot能正常启动 2、LCD驱动要根据所使用的屏幕修改。 3、NET初始…...
AI大模型:本地部署deepseek
一、安装lmstudio 1、下载网站: LM Studio - Discover, download, and run local LLMs 2、直接安装即可,记住安装的路径 二、下载deepseek模型 2.1、下载的流程 1、下载网站 https://huggingface.co/models 2、在搜索框输入:deepseek …...
(算法竞赛)图论+DFS深搜——图的dfs遍历1
题目描述 给定一个无向图,包含 n 个顶点(编号为 1 到 n)和 e 条边。要求从顶点 1 开始进行深度优先搜索(DFS),并按照访问顺序输出遍历结果。注意:当存在多个邻接点时,优先访问编号较…...
RK3588平台开发系列讲解(DMA篇)DMA engine使用
文章目录 一、DMA 使用步骤二、DMA接口2.1、DMA 通道管理相关接口2.2、DMA 描述符相关接口2.3、DMA 启动与控制接口2.4、DMA 状态检查接口2.5、 DMA 缓存管理接口2.6、DMA 中断与同步机制沉淀、分享、成长,让自己和他人都能有所收获!😄 Linux 内核的 DMA 引擎提供了一组完整…...
报名 | IEEE ICME 2025 音频编码器能力挑战赛正式开启
音频编码器是多模态大模型的重要组件,优秀的音频编码器在构建多模态系统中至关重要。在此背景下,小米集团、萨里大学、海天瑞声共同主办了 IEEE International Conference on Multimedia & Expo (ICME) 2025 Audio Encoder Capability Challenge。 …...
fputs的概念和使用案例
fputs 是 C 语言中用于向文件写入字符串的标准库函数。它与 puts 类似,但不会自动添加换行符,且支持向任意文件流(如磁盘文件、标准输出等)写入数据。 概念解析 函数原型:int fputs(const char *str, FILE *stream); …...
ASP.NET Core标识框架Identity
目录 Authentication与Authorization 标识框架(Identity) Identity框架的使用 初始化 自定义属性 案例一:添加用户、角色 案例二:检查登录用户信息 案例三:实现密码的重置 步骤 Authentication与Authorizatio…...
PFAS(全氟烷基和多氟烷基物质)测试流程详细介绍
PFAS(全氟烷基和多氟烷基物质)测试详细介绍 什么是PFAS? PFAS是(Per-and polyfluoroalkyl substances)的简称,中文名:全氟烷基和多氟烷基物质,是一系列合成有机氟化物的总称,是指至少含有一个…...
宝塔面板端口转发其它端口至MySQL的3306
最近需要把服务器的MySQL服务开放给外网,但又希望公开给所有人。也不想用默认的3306端口。同时也不想改变MySQL的默认端口。 这时候最好的办法就是用一个不常用的端口来转发至3306上去。例如使用49306至3306,外网通过49306来访问,内网依然使用…...
inquirer介绍及配合lerna在Vue中使用示例
目录 安装基本用法使用多个提示框动态选择(动态选项)表单式输入配合lerna在Vue中使用示例 Inquirer 是一个用于创建交互式命令行工具的 Node.js 库,常用于收集用户输入。它提供了多种类型的提示框,可以用于创建交互式应用程序&…...
AI商业化:如何包装技术并找到客户需求?
AI商业化:如何包装技术并找到客户需求? 适用人群:对人工智能技术有一定沉淀,正在探索技术变现和商业模式创新的创业者、技术团队以及企业管理者。同时也适合对 AI 产品包装、市场调研与用户调研感兴趣的从业人员。 一、引言 在过去几年里,从 GPT、Transformer 到 DeepSee…...
基于MODIS/Landsat/Sentinel/国产卫星遥感数据与DSSAT作物模型同化的作物产量估算
基于过程的作物生长模拟模型DSSAT是现代农业系统研究的有力工具,可以定量描述作物生长发育和产量形成过程及其与气候因子、土壤环境、品种类型和技术措施之间的关系,为不同条件下作物生长发育及产量预测、栽培管理、环境评价以及未来气候变化评估等提供了…...
OpenAI 宣布免费开放 ChatGPT 搜索,无需注册
在科技飞速发展的今天,人工智能领域的每一次突破都犹如一颗重磅炸弹,震撼着整个世界。北京时间 2025 年 2 月 6 日凌晨,OpenAI 宣布向所有用户开放 ChatGPT 搜索功能,且无需注册,这一消息瞬间引发了全球范围内的广泛关…...
如何打开vscode系统用户全局配置的settings.json
📌 settings.json 的作用 settings.json 是 Visual Studio Code(VS Code) 的用户配置文件,它存储了 编辑器的个性化设置,包括界面布局、代码格式化、扩展插件、快捷键等,是用户全局配置(影响所有…...
DeepSeek-V3本地Docker容器化部署
1. 安装Docker 确保已安装Docker Desktop for Mac: 下载并安装 Docker Desktop。 安装完成后,启动Docker Desktop。 验证安装: docker --version docker-compose --version 2. 克隆DeepSeek-V3仓库 git clone https://github.com/deeps…...
【Leetcode 每日一题】47. 全排列 II
问题背景 给定一个可包含重复数字的序列 n u m s nums nums,按任意顺序 返回所有不重复的全排列。 数据约束 1 ≤ n u m s . l e n g t h ≤ 8 1 \le nums.length \le 8 1≤nums.length≤8 − 10 ≤ n u m s [ i ] ≤ 10 -10 \le nums[i] \le 10 −10≤nums[i]≤…...
【Uniapp-Vue3】从uniCloud中获取数据
需要先获取数据库对象: let db uniCloud.database(); 获取数据库中数据的方法: db.collection("数据表名称").get(); 所以就可以得到下面的这个模板: let 函数名 async () > { let res await db.collection("数据表名称…...
【重生之学习C语言----杨辉三角篇】
目录 编辑 --------------------------------------begin---------------------------------------- 一、什么是杨辉三角? 二、问题分析 三、算法设计 使用二维数组存储杨辉三角: 递推关系: 格式化输出: 四、代码实现 完…...
天童教育:帮助孩子建立稳定的自信心
不少家长发现,自己家孩子不知道从什么时候开始,不再自信了。有些孩子在面对挑战时总是畏缩不前,不敢尝试新事物;在众人面前发言时,声音微弱,眼神闪躲。昆明天童教育认为,这些表现往往是孩子自信…...
LabVIEW自定义测量参数怎么设置?
以下通过一个温度采集案例,说明在 LabVIEW 中设置自定义测量参数的具体方法: 案例背景 假设使用 NI USB-6009 数据采集卡 和 热电偶传感器 监测温度,需自定义以下参数: 采样率:1 kHz 输入量程:0~10 V&a…...
Vim的基础命令
移动光标 H(左) J(上) K(下) L(右) $ 表示移动到光标所在行的行尾, ^ 表示移动到光标所在行的行首的第一个非空白字符。 0 表示移动到光标所在行的行首。 W 光标向前跳转一个单词 w光标向前跳转一个单词 B光标向后跳转一个单词 b光标向后跳转一个单词 G 移动光标到…...
SpringCloud详细讲解
学习目标 微服务框架SpringCloud的核心组件分布式与集群Spring Cloud 优缺点 微服务框架 微服务框架是将某个应用程序开发划分为多个小型服务独立进行业务开发的一种架构模式。以下是对微服务框架的详细介绍: 一、定义与特点 定义:微服务框架围绕业务…...
使用 OpenGL ES 在 iOS 上渲染一个四边形:从基础到实现
使用 OpenGL ES 在 iOS 上渲染一个四边形:从基础到实现 在 iOS 开发中,OpenGL ES 是一个强大的工具,用于实现高性能的 2D 和 3D 图形渲染。本文将详细分析一段完整的代码,展示如何使用 OpenGL ES 在 iOS 上渲染一个简单的四边形。…...
98.2 AI量化开发:基于DeepSeek打造个人专属金融消息面-AI量化分析师(理论+全套Python代码)
目录 0. 承前1. 金融工程结构图2. Why is DeepSeek3. 项目实现代码3.1 导入python库3.2 参数设置3.3 获取数据3.4 数据处理3.5 AI人设提示词3.6 Messages构建3.7 AI Agent3.8 response格式处理3.9 汇总函数3.10 运行案例 4. 总结4.1 系统优点4.2 系统缺点4.3 可提升方向 0. 承前…...
复制粘贴小工具——Ditto
在日常工作中,复制粘贴是常见的操作,但Windows系统自带的剪贴板功能较为有限,只能保存最近一次的复制记录,这对于需要频繁复制粘贴的用户来说不太方便。今天,我们介绍一款开源、免费且功能强大的剪贴板增强工具——Dit…...
中国人名汉语拼音字母拼写规则
中国人名汉语拼音字母拼写规则 1. Lv and Lyu2. 中国人名汉语拼音字母拼写规则References 1. Lv and Lyu LongBench: A Bilingual, Multitask Benchmark for Long Context Understanding https://arxiv.org/abs/2308.14508 2. 中国人名汉语拼音字母拼写规则 http://www.moe.g…...
