【数据结构】二叉树的模拟实现

前言:前面我们学习了堆的模拟实现,今天我们来进一步学习二叉树,当然了内容肯定是越来越难的,各位我们一起努力!

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:数据结构 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述


什么是树

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。光看文字可能大家理解不了,我们看下图。在这里插入图片描述


树的相关概念

  1. 节点的度:一个节点含有的子树的个数称为该节点的度。 如上图:A的为3。
  2. 叶节点或终端节点:度为0的节点称为叶节点; 如上图:F、G、H、I…等节点为叶节点。
  3. 非终端节点或分支节点:度不为0的节点; 如上图:B、C、D…等节点为分支节点.
  4. 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点。 如上图:A是B的父节点。
  5. 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点。 如上图:B是A的孩子节点。
  6. 兄弟节点:具有相同父节点的节点互称为兄弟节点。 如上图:B、C是兄弟节点。
  7. 树的度:一棵树中,最大的节点的度称为树的度。 如上图:树的度为5。
  8. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。
  9. 树的高度或深度:树中节点的最大层次。如上图:树的高度为4。
  10. 堂兄弟节点:双亲在同一层的节点互为堂兄弟。如上图:G、G互为兄弟节点 。
  11. 节点的祖先:从根到该节点所经分支上的所有节点。如上图:A是所有节点的祖先。
  12. 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙。
  13. 森林:由m(m>0)棵互不相交的树的集合称为森林。

什么是二叉树

二叉树是一种常见的树型数据结构,由若干个节点组成,每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树有以下特点(如图所示):

  1. 每个节点最多有两个子节点,且子节点的位置是固定的。
  2. 左子节点在父节点的左边,右子节点在父节点的右边。
  3. 二叉树的子树也是二叉树。
  4. 二叉树的每个节点都有一个值,可以是任意类型的数据。
    在这里插入图片描述

讲完了二叉树的基本性质,我们开始模拟实现二叉树吧!

二叉树的基本功能

  1. 前序遍历–根、左、右 – 深度优先遍历
  2. 计算树节点。
  3. 中序遍历–左、根、右
  4. 计算叶子节点
  5. 树的高度
  6. 求第k层结点的个数
  7. 二叉树查找值为x的结点
  8. 通过前序遍历的数组构建二叉树。
  9. 销毁二叉树
  10. 层序遍历 – 广度优先遍历
  11. 判断二叉树是否是完全二叉树

二叉树的初始化(手搓二叉树)

思路导读:由于通过数组创建二叉树比较难,我们先放在博客的后面在去讲解,但是我们又需要二叉树怎么办,那就手搓一个。
代码演示:

typedef int BTDataType;typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;}TreeNode;TreeNode* BuyNode(int x)
{TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->data = x;node->left = NULL;node->right = NULL;return node;
}TreeNode* CreateTree()//创建二叉树
{TreeNode* node1 = BuyNode(1);TreeNode* node2 = BuyNode(30);TreeNode* node3 = BuyNode(2);TreeNode* node4 = BuyNode(71);TreeNode* node5 = BuyNode(99);TreeNode* node6 = BuyNode(11);TreeNode* node7 = BuyNode(21);node1->left = node2;node1->right = node3;node2->left = node4;node2->right = node5;node3->right = node7;node3->left = node6;return node1;
}

创建好后的树的结构如下图所示
在这里插入图片描述


二叉树的前序遍历

思路导读:二叉树的前序遍历指先遍历二叉树的根,左子树,在遍历右子树。我们可以先画个图来模拟一下它遍历的顺序如下图:
在这里插入图片描述
既然图都画出来了,我们肯定思路都大致清晰了,那用什么方式去遍历呢?循环还是什么?今天我们要讲的方式是递归解决二叉树的遍历,这种是目前比较简单的方式。
代码实现:对于刚刚接触二叉树的友友们可能还不能理解这个递归的方式,没事可以看下图的递归展开图帮助你进一步理解

void PrevOrder(TreeNode* root)//前序遍历--根、左、右 -- 深度优先遍历
{if (root == NULL){return NULL;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}

在这里插入图片描述
运行结果:在这里插入图片描述


二叉树的中序遍历

思路导读:二叉树的中序遍历指先遍历左子树,再遍历根,最后遍历右子树。这个就不为大家再画递归展开图了,看代码!
代码演示

void InOrder(TreeNode* root)//中序遍历--左、根、右
{if (root == NULL){return NULL;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}

运行结果在这里插入图片描述


计算树节点

思路导读:我们要想计算树节点的个数,我们只需要将其左子树,右子树,还有根的值全部算出有多少即可,我们只需通过像前序遍历的思想来计算。如果不懂的话可以看下面的递归展开图(博主就画了前几步)。在这里插入图片描述
代码实现:

int TreeSize(TreeNode* root)//计算树节点
{if (root == NULL){return 0;}return TreeSize(root->right) + TreeSize(root->left) + 1;}

计算叶子节点

思路导读:我们可以通过遍历该树的左子树和右子树,如果左子树和右子树同时为NULL我们就让它返回1,以此类推,我们可以通过像前面一样的方式遍历二叉树达到计算叶子节点的效果(部分递归展开图)。
在这里插入图片描述
代码实现:

int TreeLeafSize(TreeNode* root)//计算叶子节点
{if (root == NULL){return 0;}if((root->left)== NULL &&(root->right) == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

计算树的高度

思路导读:计算树的高度我们可以通过比较它的左子树和右子树找出其树高度中较大的那个然后加上一即可算出来这个树的高度,怎么比较呢?那我们就可以通过利用fmax这个函数来比较其找到最大值。
(部分递归展开图如下)在这里插入图片描述
代码实现:

int TreeHight(TreeNode* root)//树给高度
{if (root == NULL){return 0;}//fmax函数,它是C语言(C99)中的一个内置函数,用于比较两个浮点数的大小并返回较大值。return fmax(TreeHight(root->left), TreeHight(root->right)) + 1;
}

求第k层节点的个数

思路导读:要想求第k层节点的个数我们需要将该层中左子树和右子树的位置分别记录下来,然后将其相加就是该层的个数。那么另一个问题来了,我们如何找到是这一层呢?我们可以通过每让它往下走一层时,就让k-1,依次递归,当k完全等于1的时候说明已经到了这一层,再返回1即可。(递归展开图如下)
在这里插入图片描述
代码实现:

int TreeLevelK(TreeNode* root, int k)//求第k层结点的个数
{assert(k > 0);if (root == NULL)return 0;if (k == 1)return 1;

二叉树查找值为x的节点

思路导读:我们通过前面写了这么多的二叉树的接口,这里我们可以想到,我们先查左子树中是否有相同的,然后我们再去查看右子树中是否有相同的,如果左子树找到了就将该值返回即可。(部分递归展开图如下)
在这里插入图片描述
代码实现:

TreeNode* TreeFind(TreeNode* root, BTDataType x)//二叉树查找值为x的结点
{if (root == NULL){return NULL;}if (root->data == x){return root;} TreeNode* cur = TreeFind(root->left,x);//先找左子树,通过指针记录,防止找到了接着往下走if (cur){return cur;}TreeNode* cur1 = TreeFind(root->right, x);//再找右子树,同理指针记录if (cur1){return cur1;}return NULL;
}

通过前序遍历的数组构建二叉树

我们先假定传来的数组为:char arr[] = { 'A','B','D','#','#','E','#','H','#','#','C','F','#','#','G','#','#' };
该’#'代表NULL,因此我们先画出其前序遍历的展开图(如下):
在这里插入图片描述
然后我们只需要像前序遍历一样,将其按照根、左子树、右子树一样依次开辟结点赋值,此时我们要注意的是一定要使用指针,防止在递归的过程中其值不会变化。
代码实现如下:

TreeNode* TreeCreate2(char* a, int* pi)//通过前序遍历的数组构建二叉树
{if (*(a + *pi) =='#'){(*pi)++;return NULL ;}TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));root->data = *(a + (*pi)++);root->left = TreeCreate2(a, pi);root->right = TreeCreate2(a, pi);return root;
}

运行结果:
在这里插入图片描述


二叉树的销毁

思路导读:二叉树的销毁可以像二叉树的后序遍历一样先左子树、右子树
代码实现:

void DestoryTree(TreeNode* root)//销毁
{if (root == NULL){return NULL;}DestoryTree(root->left);DestoryTree(root->right);free(root);}

层序遍历 – 广度优先遍历

思路导读:我们知道二叉树的一个特性,每一个节点都有俩个子节点,因此我们在可以充分的利用这个特性来实现我们层序遍历,我们可以模拟实现一个队列,让二叉树的根先存进去队列,然后打印其数据,就打印了第一层的数据,此时我们要打印第二层的数据,我们只需要将根的左子树和右子树在分别存入队列,在第二次循环中在依次打印即可实现层序遍历了。记住一定在队列中存的是二叉树根的地址而不是值(如下图所示)在这里插入图片描述
代码实现:

void levelorder(TreeNode* root)//层序遍历 -- 广度优先遍历
{QNode q1;QueueInit(&q1);// TreeHight(root);if (root){//QueuePush(Queue * pq, QDataType x)QueuePush(&q1, root);}int level = 1;while (!QueueEmpty(&q1)){while (level--){TreeNode* front = QueueFront(&q1);//将头元素地址保存在二叉树中QueuePop(&q1);printf("%c ", front->data);if (front->left){QueuePush(&q1, front->left);}if (front->right){QueuePush(&q1, front->right);}}level = QueueSize(&q1);printf("\n");}QueueDestroy(&q1);

测试函数:

void Test1()
{TreeNode* root = CreateTree1();char arr[] = { 'A','B','D','#','#','E','#','H','#','#','C','F','#','#','G','#','#' };int i = 0;levelorder(TreeCreate2(arr, &i));
}

运行结果:在这里插入图片描述


判断是否是完全二叉树

思路导读:我们可以像前面一样,让它们依次层次入队列,如果在入队列的过程中就碰到了NULL,就说明其不是完全二叉树,然后再将最后一层中队列的元素依次出队列,如果出队列的途中也碰到了NULL也就说明其不是完全二叉树。如果都没碰到则是完全二叉树。
在这里插入图片描述
代码实现

bool TreeComplete(TreeNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){TreeNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL)break;QueuePush(&q, front->left);QueuePush(&q, front->right);}// 前面遇到空以后,后面还有非空就不是完全二叉树while (!QueueEmpty(&q)){TreeNode* front = QueueFront(&q);QueuePop(&q);if (front){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


🫵🫵🫵 祝各位接下来好运连连 💞

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/232867.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vscode ssh连接不上服务器的各种解决办法

超时 可能是因为服务器太慢,等太久而报错,可以将超时时间设置长一些。步骤如下图,我将超时时间改成了60(或者更大,有时候服务器巨卡)。 参考: https://www.jianshu.com/p/0a995acf1a2e 超时&a…

操作系统的界面

(1) 请说明系统生成和系统引导的过程。 解: 系统的生成过程:当裸机启动后,会运行一个特殊的程序来自动进行系统的生成(安装),生成系统之前需要先对硬件平台状况进行检查,或者从指定文件处读取…

CogVLM与CogAgent:开源视觉语言模型的新里程碑

引言 随着机器学习的快速发展,视觉语言模型(VLM)的研究取得了显著的进步。今天,我们很高兴介绍两款强大的开源视觉语言模型:CogVLM和CogAgent。这两款模型在图像理解和多轮对话等领域表现出色,为人工智能的…

A01、关于JVM的GC回收

引用类型 对象引用类型分为强引用、软引用、弱引用,具体差别详见下文描述: 强引用:就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用&am…

35道HTML高频题整理(附答案背诵版)

1、简述 HTML5 新特性 &#xff1f; HTML5 是 HTML 的最新版本&#xff0c;它引入了很多新的特性和元素&#xff0c;以提供更丰富的网页内容和更好的用户体验。以下是一些主要的新特性&#xff1a; 语义元素&#xff1a;HTML5 引入了新的语义元素&#xff0c;像 <article&g…

GaN图腾柱无桥 Boost PFC(单相)九-EMI 滤波器容性电流影响分析

前言 为了防止 PFC 变换器中高频开关谐波对电网产生影响&#xff0c;同时抑制电网中的高频干扰对变换器运行的影响&#xff0c;一般通过在 PFC 变换器与交流电源之间加入EMI 滤波器消除共模干扰和差模干扰&#xff0c;使变换器满足相应的 EMI 标准。在基于GaN 功率器件的图腾柱…

GD32F4中断向量查询

中断向量表 中断向量对应函数 __Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ;…

管理类联考——数学——真题篇——按题型分类——充分性判断题——蒙猜C

老规矩&#xff0c;先看目录&#xff0c;平均每个3-4C&#xff08;C是月饼&#xff0c;月饼一般分为4块&#xff09; C是什么&#xff0c;是两个都不行了&#xff0c;但联合起来可以&#xff0c;联合的英文是combined&#xff0c;好的&#xff0c;我知道这个英文也记不住&#…

【Python】管理项目第三方包

我们在开发python项目时&#xff0c;如果代码每移植到到其他机器上&#xff0c;就手动 pip install XXX 安装一次&#xff0c;这样手动介入 是不是不太方便&#xff1f; 那么&#xff0c;python有像java一样的maven管理包的工具吗&#xff1f;只需要一个类似pom的文件&#xff…

学成在线bug纪录

p26&#xff1a;No converter found for return value of type: class com.xuecheng.base.model.PageResult 解决&#xff1a;给PageResult添加getter和setter方法 Illegal DefaultValue null for parameter type integer 解决&#xff1a;将swagger-spring-boot-starter依赖…

Excel怎样统计一列中不同的数据分别有多少个?

文章目录 1.打开Excel数据表2.选择“插入”&#xff0c;“数据透视表”3.选择数据透视表放置位置4.将统计列分别拖到“行”和“数值”区间5.统计出一列中不同的数据分别有多少个 1.打开Excel数据表 2.选择“插入”&#xff0c;“数据透视表” 3.选择数据透视表放置位置 4.将统计…

数据结构【1】:数组专题

一、定义 数组是编程中一种强大的数据结构&#xff0c;它允许您存储和操作相同类型元素的集合。在 Python 中&#xff0c;数组是通过数组模块创建的&#xff0c;该模块提供了一个简单的接口来创建、操作和处理数组。 二、创建数组 在 Python 中&#xff0c;可以使用内置的 a…

js DOM的一些小操作 获取节点集合Node( getElementsByClassName等)

1. getElementsByClassName(names) 返回文档中所有含有指定类名的节点 document.getElementsByClassName(a) 返回所有类名为a的节点 2.getElementsByName(name) 返回文档中所有指定name的节点。 标签可以有name属性。 3. querySelectorAll(selectors) 返回文档中所有匹配…

网络 / day04 作业

1. 基于UDP的TFTP文件传输 #include<myhead.h>//上传int do_upload(int cfd, struct sockaddr_in sin) {//定义变量存储下载请求包char buf[516] "";//定义变量存储文件名char fileName[40] "";int rfd -1;printf("请输入文件名&#xff1a;…

c 实现jpeg中的ALI(可变长度整数转换)正反向转换

用于DC的ALI表&#xff1a;DIFF 就是前后两个8X8块DC的差值&#xff0c;ssss就是DIFF值用二进制表示的位数 亮度&#xff0c;与色度的DC都是这种处理的。两个相邻的亮度与亮度比差&#xff0c;色度与色度比差产生DIFF, 扫描开始DIFF等于0。 用于AC ALI表&#xff1a;表中的AC…

喜讯!聚铭网络入选国家信息安全漏洞库(CNNVD)技术支撑单位

近日&#xff0c;国家信息安全漏洞库&#xff08;CNNVD&#xff09;公示2023年度新增技术支撑单位名单。经考核评定&#xff0c;聚铭网络正式入选并被授予《国家信息安全漏洞库&#xff08;CNNVD&#xff09;三级技术支撑单位证书》。 国家信息安全漏洞库&#xff08;CNNVD&am…

解决腾讯云CentOS 6硬盘空间不足问题:从快照到数据迁移

引言&#xff1a; 随着数据的不断增加&#xff0c;服务器硬盘空间不足变成了许多运维人员必须面对的问题。此主机运行了httpd&#xff08;apache服务&#xff09;&#xff0c;提供对外web访问服务,web资源挂载在**/data/wwwroot目录下,http日志存放在/data/wwwlogs目录下&…

11 v-bind指令

概述 v-bind指令可以说是Vue3中最常用的指令之一&#xff0c;使用v-bind&#xff0c;我们几乎能够给任何实现动态的绑定比值。 这里&#xff0c;我们主要演示以下&#xff0c;通过v-bind动态绑定CSS样式。 基本用法 我们创建src/components/Demo11.vue&#xff0c;在这个组…

Python基于joblib的并行计算

文章目录 Python基于joblib的并行计算适用场景使用示例总结爬虫&joblib使用`joblib`的场景注意事项使用实例结论joblib介绍简单示例多参数并行并行时CPU是怎么分配的何时选用并行进程&线程进程和线程之间的关系

JS逆向实战——开发者工具检测

说明&#xff1a;仅供学习使用&#xff0c;请勿用于非法用途&#xff0c;若有侵权&#xff0c;请联系博主删除 作者&#xff1a;zhu6201976 一、背景 在JS逆向领域&#xff0c;Chrome开发者工具是核心&#xff0c;抓包、调试、看调用栈等都离不开它。可以说&#xff0c;逆向人…