【初阶数据结构】详解二叉树 - 树和二叉树(三)(递归的魅力时刻)
文章目录
- 前言
- 1. 二叉树链式结构的意义
- 2. 手搓一棵二叉树
- 3. 二叉树的遍历(重要)
- 3.1 遍历的规则
- 3.2 先序遍历
- 3.3 中序遍历
- 3.4 后序遍历
- 3.5 遍历的代码实现
- 3.5.1 先序遍历代码实现
- 3.5.2 中序遍历代码实现
- 3.5.3 后序遍历代码实现
- 4. 统计二叉树结点的个数
- 5. 统计二叉树的高度
- 6. 统计二叉树的指定层数的结点个数
前言
如果有看过的堆和堆排序这篇文章的话,你一定对二叉树的顺序存储有了一定的了解,但是这个是有特定的使用环境的。
那么在本文中,我将要给大家讲解二叉树的另一种表示方式——链式结构,以及我会给大家讲解有了顺序存储结构,为什么还要使用链式存储结构?以及如何用链式创建一棵二叉树?此外,还要讲解递归在二叉树中扮演着何种角色?
1. 二叉树链式结构的意义
在学习堆时,我们说堆的本质就是一棵完全二叉树,而完全二叉树的结构注定了我们使用顺序表来存储比较方便,而这个就是二叉树的顺序存储。
可能大家也意识到了一个点:好像二叉树的顺序存储有个要求,那就是这棵二叉树必须得是完全二叉树。没错!
如果我们不分青红皂白地使用顺序结构来存储二叉树会发生什么可怕的是事情呢?
请大家将目光往下注视:
可以看到,如果我们遇到的是一棵非完全二叉树,并且我们还要有顺序结构来存储这棵树。第一步,我们就得在逻辑上将这颗树补全为完全二叉树,但是补的地方我们是不能进行任何的操作的。那这个就十分尴尬,自己创建的空间却不能愉快地使用,有点扯淡了!而且这样的方式很浪费空间资源,从上图你就可以看出有几个空间是没有办法使用的。
那该怎么办呢?此时,天空一声巨响,二叉树的链式结构就闪亮登场了!
二叉树的链式结构针对的群体就是普通的二叉树。 那至于普通二叉树链式是如何被我们用代码创建的,让我们接着往下看!
2. 手搓一棵二叉树
这里为了降低大家的学习成本,我先不教大家如何用函数创建二叉树,如果大家对自己的二叉树比较有自信的话,可以移步到本文的后面,学习如何用函数创建一棵二叉树。
学习是要一步一步来的,我们不能不懂装懂,有时候也得直面不完美的自己!
二叉树链式结构示意图:
那我们这里开始就开始手搓一棵二叉树,为了方便大家学习,我会按照下面的图的给大家建立,往后的操作都是以这副图为基础。
代码如下:
//二叉树结点的结构体
typedef int BTDataType;typedef struct BTNode
{BTDataType data;struct BTNode* left;struct BTNode* right;}BTNode;BTNode* BuyNewnode(BTDataType x)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));newnode->data = x;newnode->left = NULL;newnode->right = NULL;return newnode;
}BTNode* CreateTree()
{BTNode* node1 = BuyNewnode(1);BTNode* node2 = BuyNewnode(2);BTNode* node3 = BuyNewnode(3);BTNode* node4 = BuyNewnode(4);BTNode* node5 = BuyNewnode(5);BTNode* node6 = BuyNewnode(6);//开始结点之间的链接node1->left = node2;node1->right = node3;node2->left = node4;node2->right = node5;node3->right = node6;return node1;
}
这种方式初学者来说十分的友好。
3. 二叉树的遍历(重要)
接下来,我来讲一下本文的精华 —— “二叉树的遍历”。为什么会说是精华的部分呢?因为想要学会这个就得先知道递归的规则,并且这是科班同学在练习中会经常遇到的题目。同时这个也是二叉树的入门门槛!
二叉树遍历有四种方式,分别是:
- 先序遍历(可能有的书籍会称其为"先根遍历")
- 中序遍历
- 后序遍历
- 层序遍历
3.1 遍历的规则
- 先序遍历:遍历顺序为根、左子树、右子树。记忆的规则就是,先序又称先根,顾名思义就是根结点先遍历。
- 中序遍历:遍历顺序为左子树、根、右子树。记忆的规则就是,中序是将根结点放在遍历的中间顺序。
- 后序遍历:遍历顺序为左子树、右子树、根。记忆的规则就是,后序是将根节点但在遍历的最后顺序。
- 层序遍历:遍历的顺序时按照一层层开始,每一层从左往右依次遍历各节点。
在本文,会重点讲解先序遍历、中序遍历以及后序遍历。
为了更好的讲解每种遍历的规则,接下来我会分篇幅给大家做进一步的讲解。
3.2 先序遍历
先序遍历:遍历顺序为根、左子树、右子树。
任何一颗树都是由根节点及其子树所构成的。二叉树也是如此,二叉树由其根节点、左子树和右子树所构成。其中,左子树由其根节点、左子树和右子树所构成…。给人一种循环的感觉,而这个就是递归!大家可以从下图(只做了部分的划分)感受一下
好了,我们根据先序遍历的规则,写出它的数据遍历的顺序(注意:空树用NULL表示)。
先序遍历的顺序:1 2 4 NULL NULL 5 NULL NULL 3 NULL 6 NULL NULL
怎么样,不是很困难吧。
3.3 中序遍历
中序遍历:遍历顺序为左子树、根、右子树
趁热打铁,我们把中序遍历也讲了。
有了先序遍历的经验,中序遍历洒洒水啦!
这里我就直接写答案了:
中序遍历的顺序:NULL 4 NULL 2 NULL 5 NULL 1 NULL 3 NULL 6 NULL
3.4 后序遍历
后序遍历:遍历顺序为左子树、右子树、根。
后序遍历的顺序:NULL NULL 4 NULL NULL 5 2 NULL NULL NULL 6 3 1
3.5 遍历的代码实现
这个就体现出递归的魅力时刻了!
这里我会拿先序遍历作为重点讲解,其余遍历方式的思维都是一样的。
3.5.1 先序遍历代码实现
先序遍历:遍历顺序为根、左子树、右子树。
//先序遍历
void PreOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%d ",root->data);PreOrder(root->left);PreOrder(root->right);
}
当遇到一个递归却被深深绕进去时,最好画一个递归展开图!
3.5.2 中序遍历代码实现
中序遍历:遍历顺序为左子树、根、右子树
//中序遍历
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%d ",root->data);InOrder(root->right);
}
3.5.3 后序遍历代码实现
后序遍历:遍历顺序为左子树、右子树、根。
//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}
可能大家在平常的练习中看到的是这种形式的,
要想出来这个效果,我们只需要把printf("NULL ")
这个语句注释掉即可。但是需要提醒大家的一点就是,上面这幅图的结果并不是二叉树原本的模样,大家要理解本质!
4. 统计二叉树结点的个数
在这里我要给大家将一个非常重要的思想 —— “分治思想”。
给大家设置一个场景,假如现在我是一位大学的校长,我要统计学校的人数。我该怎么做?
第一种方法:我到学生宿舍一个一个人的统计人数,每当宿舍有一个新生时,我就在我的小本本上画"正"字的一个笔画。最后我只要统计小本本上有多少个正字即可。
但是这个方法非常的费"校长"。不经的会感叹到这个校长当得也太累了吧!
第二种方法:既然身为一校之长,我肯定有一定的权力。我就让每个院的院长汇报各自院的人数给我,我再统计不就好了。院长接到任务后,就命令每个专业的系主任汇报各专业人数给我。每个系主任又叫每个班的班长统计各班的人数给他。之后,以宿舍为单位,让宿舍长汇报宿舍人数给班长,班长再做汇总,将结果给系主任。
这不就纯纯是打工人体系!!!
汇报人数的情况:
有了这个思想之后,我们写代码!
int TreeSize(BTNode* root)
{//写法一if(root == NULL){return 0;}return TreeSize(root->left) + TreeSize(root->right) + 1;/* //写法二return root == NULL? 0: TreeSize(root->left) + TreeSize(root->right) + 1;*/
}
5. 统计二叉树的高度
用了分治思想,这个代码似乎也能理解了。
int TreeHeight(BTNode* root)
{if(root == NULL){return 0; }int left_height = TreeHeight(root->left);int right_height = TreeHeight(root->right);return left_height > right_height? left_height + 1:right_height + 1;}
6. 统计二叉树的指定层数的结点个数
这里给大家提供一个思路:当前K层结点的个数 = 对应左子树K-1层结点的个数 + 对应右子树K-1层结点的个数
int TreeKLevel(BTNode* root, int k)
{if(root == NULL){return 0;}if(k == 1){return 1;}int leftK = TreeKLevel(root->left,k-1);int rightK = TreeKLevel(root->right,k-1);return leftK + rightK;
}
好了,本文就到这里结束了!
本文主要讲解了二叉树链式结构的定义以及接口的实现,其中有个重要的思想可以帮助我们更快的理解递归背后的本质,体会到递归的魅力。
最后如果觉得本文写的不错的话,麻烦给偶点个赞吧!!!
相关文章:

【初阶数据结构】详解二叉树 - 树和二叉树(三)(递归的魅力时刻)
文章目录 前言1. 二叉树链式结构的意义2. 手搓一棵二叉树3. 二叉树的遍历(重要)3.1 遍历的规则3.2 先序遍历3.3 中序遍历3.4 后序遍历3.5 遍历的代码实现3.5.1 先序遍历代码实现3.5.2 中序遍历代码实现3.5.3 后序遍历代码实现 4. 统计二叉树结点的个数5.…...

【QT】QWidget 重要属性
文章目录 enabledgeometrywindowTitlewindowIconqrc 机制windowOpacitycursorfontQFont toolTip 和 toolTipDurationfocusPolicyQt::FocusPolicy styleSheet enabled 作用:设置控件是否可使用. true 表⽰可用, false 表⽰禁用. 对应的API bool isEnabled(); // 获…...

什么是数据库连接池?为什么需要使用连接池?
什么是数据库连接池?为什么需要使用连接池? 什么是数据库连接池? 数据库连接池是一种创建和管理数据库连接的技术。在传统的应用程序中,每当需要与数据库进行交互时,都会创建一个新的数据库连接。 这种做法虽然简单…...

2024ICPC网络赛第一场C. Permutation Counting 4(线性代数)
题目链接 题目大意:给你n个范围[ l i , r i l_i,r_i li,ri],每个位置可以在这个范围中选择一个数,然后形成排列1到n的排列p。问p的所有情况的个数的奇偶性。 一个很妙的行列式转化,纯纯的线性代数。 首先,我们把…...

01.前端面试题之ts:说说如何在Vue项目中应用TypeScript?
文章目录 一、前言二、使用Componentcomputed、data、methodspropswatchemit 三 、总结 一、前言 与link类似 在VUE项目中应用typescript,我们需要引入一个库vue-property-decorator, 其是基于vue-class-component库而来,这个库vue官方推出…...

【HTTP】方法(method)以及 GET 和 POST 的区别
文章目录 方法(method)登录上传GET 和 POST 有什么区别(面试)区别不准确的说法 方法(method) 首行中的第一部分。首行是由方法、URL 和版本号组成 方法描述了这次请求想干什么,最主要的是&…...

Ubuntu NFS 搭建及配置
在 Ubuntu 上搭建和配置 NFS(Network File System)服务器,可以让其他设备通过网络访问共享的文件夹。以下是步骤指南: 1. 安装 NFS 服务器 首先,安装 NFS 服务器软件包: sudo apt update sudo apt insta…...

双十一好物推荐,这些值得入手的宝藏产品
随着双十一的钟声即将敲响,这个万众期待的购物盛宴就要来临!为了让大家避免在众多的商品中不知所措,妮妮精心筹备了一份购物清单,分享那些我亲身感受超棒,觉得十分值得购买的物品。 这些商品不但价格合理,而…...

秋招内推2025--招联金融
【投递方式】 直接扫下方二维码,或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus,使用内推码 igcefb 投递) 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…...

C++类和对象——第二关
目录 类的默认成员函数: (一)构造函数 (二)析构函数 (三)拷贝构造函数 类的默认成员函数: 类里面有6个特殊的成员函数分别包揽不同的功能; (一)构造函数…...

服务器数据恢复—raid5阵列热备盘上线失败导致阵列崩溃的数据恢复案例
服务器磁盘阵列数据恢复环境: 服务器中有两组分别由4块SAS硬盘组建的raid5磁盘阵列,两组raid5阵列划分LUN,组成LVM结构,格式化为EXT3文件系统。 服务器磁盘阵列故障: 服务器中一组raid5阵列中有一块硬盘离线ÿ…...

Python与SQL Server数据库结合导出Excel并做部分修改
Python与SQL Server数据库结合导出Excel并做部分修改 需求:在数据库中提取需要的字段内容;并根据字段内容来提取与拆分数据做为新的列最后导出到Excel文件 # -*- coding: utf-8 -*- import pandas as pd import re import pymssql import timestart_ti…...

常见的TTL,RS232,RS485,IIC,SPI,UART之间的联系和区别
简单总结 图片来源 RS232,RS485可参考,IIC,SPI,UART可参考 烧录程序中常听到的一句话就是USB转TTL,但严格来说算是USB传输数据的协议转换成TTL(Transistor-Transistor Logic)协议传输数据。首先,usb是常见…...

【数据结构】栈和队列(Stack Queue)
引言 在对顺序表,链表有了充分的理解之后,现在让我们学习栈和队列!!! 【链表】 👈链表 【顺序表】👈顺序表 目录 💯栈 1.栈的概念及结构 2.栈的实现 ⭐初始化栈 ⭐入栈 ⭐…...

Vue.js基础
Vue.js https://v2.cn.vuejs.org/https://cn.vuejs.org/初识Vue 官网:Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层…...

罐区紧急切断阀安装位置规范
在化工生产与储存的复杂环境中,罐区紧急切断阀的安装位置规范不仅是保障生产安全的关键一环,更是预防重大事故、减少损失的有效手段。在深入理解了罐区布局、物料特性及潜在风险后,对于紧急切断阀的安装位置,我们应遵循以下更为细…...

JavaScript 中的事件模型
JavaScript 中的事件模型是浏览器如何处理用户交互(如点击、键盘输入、鼠标移动等)或其他事件(如加载完成、定时器等)的机制。理解事件模型有助于我们处理这些事件并响应它们。JavaScript 的事件模型主要包括以下几个部分…...

理解Java引用数据类型(数组、String)传参机制的一个例子
目录 理解Java引用数据类型(数组、String)传参机制的一个例子理解样例代码输出 参考资料 理解Java引用数据类型(数组、String)传参机制的一个例子 理解 引用数据类型传递的是地址。用引用类型A给引用类型B赋值,相当于…...

【计算机组成原理】实验一:运算器输入锁存器数据写实验
目录 实验要求 实验目的 主要集成电路芯片及其逻辑功能 实验原理 实验内容及步骤 实验内容 思考题 实验要求 利用CP226实验箱上的K16~K23二进制拨动开关作为DBUS数据输入端,其它开关作为控制信号的输入端,将通过K16~K23设定…...

LSI SAS 9361-8i和SAS3008 12 gb / s PCIe 3.0 RAID 阵列卡配置
LSI SAS 9361-8i和SAS3008 12 gb / s PCIe 3.0 RAID 阵列卡配置 开机,BIOS自检,可以看到设备硬盘信息,以及提示CtrlR进入Raid卡配置界面。 按CtrlR进入Raid卡配置界面,一般来说使用CtrlR进入Raid卡配置界面的Raid卡配置都通用。 …...

node js版本低导致冲突WARN EBADENGINE package: required: { node: ‘>=18‘ }
重新安装依赖包 1、删除旧的 node_modules 目录和 package-lock.json 文件: rm -rf node_modules rm package-lock.json2、升级node版本 wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bashexport NVM_DIR"$([ -z "${…...

828华为云征文|使用Flexus X实例安装宝塔面板教学
目录 一、Flexus X实例简介 1.1 概述 1.2 产品规格 二、切换操作系统 2.1 Huawei Cloud EulerOS 2.0 标准版 2.2 切换镜像 三、部署宝塔面板 3.1 安装宝塔面板 3.2 放通安全组规则 3.3 登录宝塔面板 四、使用感受 4.1 柔性算力随心配 4.2 一直加速一直快 4.3 越用…...

1.量化第一步,搭建属于自己的金融数据库!
数据是一切量化研究的前提。 做量化没有数据,就相当于做饭时没有食材。 很多时候,我们需要从大量的数据中寻找规律,并从中开发出策略。如果我们每次使用的时候,都从网上去找数据,一方面效率低下,另一方面短…...

git-repo系列教程(6) 在自己服务器上搭建git-repo仓库
为什么要在自己的服务器上搭建git-repo仓库呢? 因为 清华大学开源软件镜像站 有时会更新同步git repo,导致不能使用.可能在局域网不能访问外网,无法下载镜像站上的git-repo仓库完全版. 操作步骤 1.获取git-repo仓库 需要先下载完全的仓库 cd .repo/repo/ #获取镜像站上的…...

微服务——服务保护(Sentinel)(一)
1.雪崩问题 级联失败或雪崩问题指的是在微服务架构中,由于服务间的相互依赖和调用,当一个服务出现故障时,会引起调用它的服务也出现故障,进而引发整个调用链路的多个服务都出现故障,最终导致整个系统崩溃的现象。 产生…...

jenkins声明式流水线语法详解
最基本的语法包含 pipeline:所有有效的声明式流水线必须包含在一个 pipeline 块中stages:包含一系列一个或多个stage指令stage:stage包含在stages中进行,比如某个阶段steps:在阶段中具体得执行操作,一个或…...

mini-lsm通关笔记Week2Overview
Week 2 Overview: Compaction and Persistence 在上周,您已经实现了LSM存储引擎的所有必要结构,并且您的存储引擎已经支持读写接口。在本周中,我们将深入探讨SST文件的磁盘组织,并研究在系统中实现性能和成本效益的最佳方法。我们…...

基于SpringBoot的在线点餐系统【附源码】
基于SpringBoot的高校社团管理系统(源码L文说明文档) 4 系统设计 4.1 系统概述 网上点餐系统的结构图4-1所示: 图4-1 系统结构 模块包括主界面,首页、个人中心、用户管理、美食店管理、美食分类管理、美食…...

生成式语言模型底层技术面试
在准备生成式语言模型的底层技术面试时,可以关注以下几个关键领域: 1. 模型架构 Transformer架构:了解自注意力机制、编码器-解码器结构,以及如何处理序列数据。预训练与微调:解释预训练和微调的过程,如何…...

HTML开发指南
目录 一、HTML基础1. HTML简介(1)标记语言(2)结构化文档(3)标签与属性(4)注释(5)版本演变 2. HTML文档结构(1)文档类型声明࿰…...