当前位置: 首页 > news >正文

【初阶数据结构】详解二叉树 - 树和二叉树(三)(递归的魅力时刻)

文章目录

  • 前言
  • 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 遍历的规则

  1. 先序遍历:遍历顺序为根、左子树、右子树。记忆的规则就是,先序又称先根,顾名思义就是根结点先遍历。
  2. 中序遍历:遍历顺序为左子树、根、右子树。记忆的规则就是,中序是将根结点放在遍历的中间顺序。
  3. 后序遍历:遍历顺序为左子树、右子树、根。记忆的规则就是,后序是将根节点但在遍历的最后顺序。
  4. 层序遍历:遍历的顺序时按照一层层开始,每一层从左往右依次遍历各节点。

在本文,会重点讲解先序遍历、中序遍历以及后序遍历。

为了更好的讲解每种遍历的规则,接下来我会分篇幅给大家做进一步的讲解。

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阵列中有一块硬盘离线&#xff…...

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 的核心库只关注视图层&#xf…...

罐区紧急切断阀安装位置规范

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

JavaScript 中的事件模型

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

理解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卡配置都通用。 …...

Vxe-Table横向也能无限滚?搞定超宽表格列动态加载的完整配置指南

Vxe-Table横向无限滚动实战:超宽表格列动态加载的终极解决方案 在金融分析、数据报表和动态表单等场景中,前端开发者经常面临一个棘手问题:如何处理字段数量可能无限增长的宽表格?传统分页方式会割裂数据连续性,而一次…...

OpenClaw+GLM-4.7-Flash隐私方案:本地化处理敏感数据

OpenClawGLM-4.7-Flash隐私方案:本地化处理敏感数据 1. 为什么需要本地化隐私方案 去年我在帮一家诊所设计病历管理系统时,遇到了一个棘手问题:他们需要自动化处理患者检查报告,但又担心将敏感数据上传到云端存在泄露风险。这促…...

【obs studio】从零开始:高效录制屏幕与声音的完整指南

1. 为什么选择OBS Studio录制屏幕与声音? 如果你正在寻找一款免费、开源且功能强大的屏幕录制工具,OBS Studio绝对是你的不二之选。我最初接触这款软件是因为需要录制一些技术教程,试过市面上不少付费软件后,发现OBS Studio不仅完…...

金融数据清洗总出错?(Pandas+OpenBB+YFinance联合清洗框架首次公开)

第一章:金融数据清洗总出错?(PandasOpenBBYFinance联合清洗框架首次公开) 金融数据清洗常因缺失值、时区错位、字段命名不一致、多源数据时间对齐失败等问题导致回测失真或模型训练崩溃。传统单库处理方式难以兼顾实时性、标准化与…...

从下载到运行:Qwen-Image-Edit-2511量化模型一站式部署教程

从下载到运行:Qwen-Image-Edit-2511量化模型一站式部署教程 1. 环境准备与快速部署 Qwen-Image-Edit-2511作为Qwen-Image-Edit-2509的增强版本,在图像编辑任务中展现出更强大的能力。但对于大多数开发者而言,如何快速部署这个模型才是当务之…...

拆解RoboteX AVATAR机器人:4个电机如何驱动履带+摇臂?一份紧凑传动布局的保姆级图解

RoboteX AVATAR机器人传动系统深度解析:四电机协同驱动履带与摇臂的机械艺术 当第一次看到RoboteX AVATAR Tactical Robot在复杂地形中自如穿梭的视频时,很难不被它那看似简单却异常高效的移动方式所吸引。这款战术机器人的核心秘密,就藏在它…...

GTX1060老显卡也能跑PyTorch!保姆级Win10+CUDA11.3+cudnn8.2环境配置避坑实录

GTX1060老显卡深度学习环境搭建全指南:从驱动优化到PyTorch实战 手里还握着五年前入手的GTX1060显卡?别急着让它退役。这套经典的Pascal架构显卡依然能在深度学习入门阶段大显身手。本文将带你完整走通Win10系统下的CUDA 11.3 cuDNN 8.2 PyTorch 1.11…...

从桁架到螺栓:HM-3420在汽车后桥装配中的实战应用

HM-3420螺栓连接技术在汽车后桥装配中的创新实践 汽车后桥作为承载车身重量与传递动力的关键部件,其结构强度直接关系到整车安全性能。在传统装配工艺中,桁架连接往往面临应力集中、疲劳寿命不足等挑战。HM-3420螺栓连接系统的出现,为这一领域…...

2023年VSCode插件开发全指南:从零发布你的第一个扩展(TypeScript版)

2023年TypeScript生态下的VSCode插件开发实战 在当今开发者工具生态中,Visual Studio Code以其轻量化和高度可扩展性占据了绝对领先地位。根据2023年Stack Overflow开发者调查报告,VSCode以74.48%的使用率成为最受欢迎的代码编辑器。而插件系统正是其生态…...

知识点总结--day09(Mybatis及Mybatis-Plus)

目录 1、系统架构流程? 2结果集映射? 3mapper传参? 4、xml常用配置 5、缓存机制 6、分页插件 7、Mybatis-Plus常用API 末尾页 1、系统架构流程? 执行过程: mybatis配置 mybatis-config.xml,名称可变,此文件作为mybatis的全局配置…...