【追梦之旅】—— 手“C”二叉树~

news2024/12/23 10:58:42

【追梦之旅】—— 手“C”二叉树~😎

  • 前言🙌
    • 什么是二叉树?!
    • 特殊的二叉树
    • 二叉树的性质
    • 二叉树的存储结构
    • 二叉树链式结构的实现
      • 二叉树的链式结构:
      • 二叉树的创建。
    • 二叉树的遍历
      • 前序遍历(先根遍历)
      • 中序遍历(中根遍历)
      • 后序遍历(后根遍历)
      • 层序遍历
    • 二叉树的经典玩法:
      • 二叉树结点个数
      • 二叉树叶子结点个数
      • 二叉树查找值为x的节点
      • 二叉树第k结点个数
      • 判断二叉树是否是完全二叉树
  • 总结撒花💞

追梦之旅,你我同行

   
😎博客昵称:博客小梦
😊最喜欢的座右铭:全神贯注的上吧!!!
😊作者简介:一名热爱C/C++,算法等技术、喜爱运动、热爱K歌、敢于追梦的小博主!

😘博主小留言:哈喽!😄各位CSDN的uu们,我是你的博客好友小梦,希望我的文章可以给您带来一定的帮助,话不多说,文章推上!欢迎大家在评论区唠嗑指正,觉得好的话别忘了一键三连哦!😘
在这里插入图片描述

前言🙌

    哈喽各位友友们😊,我今天又学到了很多有趣的知识现在迫不及待的想和大家分享一下!😘我仅已此文,手把手带领大家学习如何**手“C”二叉树** 都是精华内容,可不要错过哟!!!😍😍😍

什么是二叉树?!

csdn InsCodeAl的解释:二叉树是一种树形数据结构,它由一些节点和连接这些节点的边组成。每个节点最多有两个子节点,其中一个是左子节点,另一个是右子节点。节点没有子节点的被称为叶子节点。二叉树的根节点是树的顶部节点,它没有父节点。二叉树可以被用来表示表达式、文件系统、编译器语法分析树等等。
我的理解:
一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空
  2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成

在这里插入图片描述
3. 二叉树不存在度大于2的结点
比特科技
4. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
5.二叉树有以下几种类型:
在这里插入图片描述

特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k - 1 ,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。简单来说,就是左子树可以没有右子树,但是有右子树一定有左子树。要注意的是满二叉树是一种特殊的完全二叉树

二叉树的性质

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2^(i - 1)个结点.
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是 2^h - 1.
  3. 对任何一棵二叉树, 如果度为0其叶结点个数为N0 , 度为2的分支结点个数为N2 ,则有 N0= N2+1
  4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log(n + 1) . (ps: 是log以2
    为底,n+1为对数)
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
  6. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
  7. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
  8. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。

  1. 顺序存储
    顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树

在这里插入图片描述

  1. 链式存储
    二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,红黑树会用到三叉链。

二叉树链式结构的实现

二叉树的链式结构:

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

二叉树的创建。

这里利用前序遍历来创建我们的二叉树,源码如下:

BTNode* CreateNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!\n");
		return NULL;
	}
	newnode->_data = x;
	newnode->_left = NULL;
	newnode->_right = NULL;
	return newnode;
}
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	if (n == 0)
	{
		*pi = 0;
		return NULL;
	}
	if (*a == '#')
	{
		*pi = 1;
		return NULL;
	}
	int leftSize = 0;
	int rightSize = 0;
	BTNode* root = CreateNode(*a);
	root->_left = BinaryTreeCreate(a + 1, n - 1, &leftSize);
	root->_right = BinaryTreeCreate(a + 1 + leftSize, n - 1 - leftSize, &rightSize);

	*pi = leftSize + rightSize + 1;
	return root;
}

CreateNode函数是开辟结点的函数,利用malloc函数在堆区申请结点空间。我们在这里提供一个字符数组传递到 BinaryTreeCreate(BTDataType a, int n, int pi)函数,字符中的‘#’表示为空

该树的形状如下图所示:
在这里插入图片描述

二叉树的遍历

我们要把一个数递归分为根、左子树、右子树。
在这里插入图片描述

前序遍历(先根遍历)

遍历规律: 根 -->左子树–>右子树

源码展示:

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf(" # ");
		return;
	}
	printf(" %c ", root->_data);
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}

中序遍历(中根遍历)

遍历规律: 左子树–> 根–> 右子树
源码展示:

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("#");
		return;
	}
	
	BinaryTreePrevOrder(root->_left);
	printf(" %c ", root->_data);
	BinaryTreePrevOrder(root->_right);
}

后序遍历(后根遍历)

遍历规律: 左子树–>右子树–>根
源码展示:

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("#");
		return;
	}

	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
	printf(" %c ", root->_data);
}

层序遍历

遍历规律: 从上往下,从左至右 ,一层一层遍历

这里实现的层序遍历,是利用队列数据结构来实现的。采用的算法思想是:上一层带下一层,简单的来说就是父亲带孩子。先将根节点入队列,然后出队列时将它的孩子都入队列,然后递归。当队列为空时,说明已经遍历完毕。

源码展示:

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* Front = QueueFront(&q);
		QueuePop(&q);
		printf(" %c ", Front->_data);
		if (Front->_left)
		{
			QueuePush(&q, Front->_left);
		}
		if (Front->_right)
		{
			QueuePush(&q, Front->_right);
		}

	}
	printf("\n");
	QueueDestroy(&q);
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	if (root == NULL)
		return true;
	if ((root->_left == NULL) && (root->_right == NULL))
	{
		return true;
	}
	if (root->_left == NULL)
	{
		return false;
	}
	return BinaryTreeComplete(root->_left) && BinaryTreeComplete(root->_right);
}


二叉树的经典玩法:

二叉树结点个数

求解二叉树结点的个数,我们可以利用递归的思想。要想求整棵树的结点,就先求出这棵树左子树和右子树的结点个数,然后再加上自己,就是整棵树的结点个数了。

源码分享:

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return BinaryTreeSize(root->_left)
		+ BinaryTreeSize(root->_right) + 1;
}

二叉树叶子结点个数

同样的,利用递归。将问题划分为一个一个子问题。求整棵树的叶子结点,其实就是求出其左子树 + 右子树的叶子结点。

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if ((root->_left == NULL) && (root->_right == NULL))
		return 1;
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

二叉树查找值为x的节点

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->_data == x)
		return root;
	BTNode* left_find = BinaryTreeFind(root->_left, x);
	if (left_find != NULL)
		return left_find;
	BTNode* right_find = BinaryTreeFind(root->_right, x);
	if (right_find != NULL)
		return right_find;
	return NULL;
}

二叉树第k结点个数

// 二叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->_left, k - 1)
		+ BinaryTreeLevelKSize(root->_right, k - 1);
}

判断二叉树是否是完全二叉树

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	if (root == NULL)
		return true;
	if ((root->_left == NULL) && (root->_right == NULL))
	{
		return true;
	}
	if (root->_left == NULL)
	{
		return false;
	}
	return BinaryTreeComplete(root->_left) && BinaryTreeComplete(root->_right);
}

运行结果截图:

这是我们的二叉树:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
显然,我们的测试结果都是正确哒~

总结撒花💞

   本篇文章旨在分享二叉树的相关知识,以及如何手“C”二叉树 。希望大家通过阅读此文有所收获!完整代码我就不发出来啦,有需要的可以私信我~
   😘如果我写的有什么不好之处,请在文章下方给出你宝贵的意见😊。如果觉得我写的好的话请点个赞赞和关注哦~😘😘😘

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

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

相关文章

java springboot yml文件配置 多环境yml

如果是properties改用yml&#xff0c;直接改后缀&#xff0c;原文件中的配置语法改用yml的语法即可&#xff0c;系统会自动扫描application.properties和application.yml文件&#xff08;注意&#xff1a;改了之后需要maven 命令 clean一下&#xff0c;清个缓存&#xff09;。 …

VSOMEIP3抓包数据

环境 $ cat /etc/os-release NAME"Ubuntu" VERSION"20.04.6 LTS (Focal Fossa)" IDubuntu ID_LIKEdebian PRETTY_NAME"Ubuntu 20.04.6 LTS" VERSION_ID"20.04" HOME_URL"https://www.ubuntu.com/" SUPPORT_URL"https:/…

简洁灵活工单管理系统,支持工单模版字段、工单状态自定义

一、开源项目简介 本项目为FeelDesk工单管理系统的开源版&#xff08;OS&#xff09;&#xff0c;是基于开发者版&#xff08;DEV&#xff09;分离的标准版&#xff1b;支持工单模版字段、工单状态等自定义&#xff0c;可为不同的模版设置不同的路由规则&#xff1b;对工单需求…

信号在MATLAB中的运算——信号的相加相乘

信号在MATLAB中的运算——信号的相加相乘 信号的相加和相乘是指同一时刻信号取值的相加和相乘。 对于离散序列来说&#xff0c;序列相加是将信号对应时间序列的值逐项相加&#xff0c;在这里不能像连续时间信号那样用符号运算来实现&#xff0c;而必须用向量表示的方法&#…

Python获取北上广深历史天气数据并做数据可视化

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 知识点: 动态数据抓包 requests发送请求 结构化非结构化数据解析 开发环境: 开发环境: python 3.8 运行代码 pycharm 2022.3.2 辅助敲代码 专业版 模块使用&#xff1a; requests 发送请求 pip install requests par…

2.2.2 部署Master节点、添加Node节点

2.2.2 部署Master节点 1.安装docker、kubeadm、kubelet、kubectl 前面我们已经完成了虚拟机中系统的初始化&#xff0c;下面我们就在我们所有的节点上安装docker、kubeadm、kubelet、kubectl。 Kubernetes默认CRI&#xff08;容器运行时&#xff09;为Docker&#xff0c;因此…

【JavaScript】拾遗(5.25)

文章目录 1. JavaScript2.HTML嵌入JS的第一种方式:行间事件3.HTML嵌入JS的第二种方式:脚本块的方式4. HTML嵌入JS的第三种方式:外部式(外链式)5. 局部变量和全局变量6. 函数7.事件8.回调函数8.1 注册事件8.2 代码的执行顺序 1. JavaScript JavaScript是一门脚本语言。&#xf…

微服务开发系列 第六篇:Redisson

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

动态规划-分割回文串 II

动态规划-分割回文串 II 1 题目描述2 示例2.1 示例 1&#xff1a;2.2 示例 2&#xff1a;2.3 示例 3&#xff1a;2.4 提示&#xff1a; 3 解题思路和方法3.1 解题思路3.1.1 确定状态3.1.2 转移方程3.1.3 初始条件和边界情况3.1.4 计算顺序3.1.5 回文串的判断方法 3.2 算法代码实…

day17 - 用形状包围图像

在进行图像轮廓提取时&#xff0c;有的情况下不需要我们提取出精确的轮廓&#xff0c;只要提取出一个接近于轮廓的近似多边形&#xff0c;就可以满足后续的操作。 本期我们来学习如何通过设置参数来找出图像的近似多边形。 完成本期内容&#xff0c;你可以&#xff1a; 了解…

《汇编语言》- 读书笔记 - 第5章- [BX]和 loop 指令

《汇编语言》- 读书笔记 - 第5章- [BX]和 loop 指令 5.1 [BX]问题 5.1 5.2 Loop 指令任务 1任务 2任务 3程序 5.1问题 5.2问题 5.2 5.3 在 Debug 中跟踪用 loop 指令实现的循环程序5.4 Debug 和汇编编译器 masm 对指令的不同处理DebugMASM 5.5 loop 和[bx]的联合应用程序 5.5问…

大数据项目实战之数据仓库:电商数据仓库系统——第9章 数仓开发之DWD层

文章目录 第9章 数仓开发之DWD层9.1 交易域加购事务事实表9.2 交易域下单事务事实表9.3 交易域取消订单事务事实表9.4 交易域支付成功事务事实表9.5 交易域退单事务事实表9.6 交易域退款成功事务事实表9.7 交易域购物车周期快照事实表9.8 工具域优惠券领取事务事实表9.9 工具域…

Python论文复现:VMD之自适应选择分解模态数K值

Python论文复现&#xff1a;《基于稀疏指标的优化变分模态分解方法》 信号分解方法中&#xff0c;虽然变分模态分解&#xff08;Variational Mode Decomposition, VMD&#xff09;有严格的数学推导&#xff0c;能有效抑制端点效应、模态混叠等问题&#xff0c;但其分解模态数需…

Docker笔记9 | Docker中网络功能知识梳理和了解

9 | Docker中网络功能知识梳理和了解 1 外部访问容器1.1 访问方式1.2 映射所有接口地址1.3 映射到指定地址的指定端口1.4 映射到指定地址的任意端口1.5 查看映射端口配置 2 容器互联2.1 新建网络2.2 连接容器 3 配置DNS 简单说&#xff1a;Docker 允许通过外部访问容器或容器互…

Python中的全局变量与局部变量

1 定义 全局变量指的是在整个python文件中定义的变量&#xff0c;而局部变量是在函数内部定义的变量。 a 1 def yang_func():b 2 从以上代码中可以看出&#xff0c;变量a是全局变量&#xff0c;变量b是定义在yang_func()函数的内部&#xff0c;因此b是局部变量。 2 使用范…

云原生之部署Docker管理面板SimpleDocker

云原生之部署Docker管理面板SimpleDocker 一、SimpleDocker介绍1. SimpleDocker简介2. SimpleDocker特点 二、本地环境介绍1. 本地环境规划2. 本次实践介绍 三、本地环境检查1.检查Docker服务状态2. 检查Docker版本3.检查docker compose 版本 四、下载SimpleDocker镜像五、部署…

【GD32开发】一、GD32F103 TIMER0 PWM死区时间计算

一、PWM死区时间如何计算&#xff1f; GD32F103的数据手册关于死区时间控制的公式如上图。 DTCFG的值为 设置死区结构体的寄存器值 tDTS_CK的值为 系统时钟的时钟周期。 如&#xff1a;GD32F103的系统时钟是108Mhz, 则tDTS_CK 1/108Mhz 9.26ns。( stm32的这个值跟定时器的…

如何从 Android 内部存储中恢复已删除的照片?

我们使用手机录制的照片和视频通常存储在手机的内存中。我们存储它们以记住我们生活的美丽。然而&#xff0c;在管理这些照片的过程中&#xff0c;一些用户却发现自己不小心删除了这些照片&#xff0c;这很尴尬。 如果您的 Android 设备遇到过此类问题&#xff0c;那么您来对地…

SpringBoot日记本系统小白部署指南

哈喽&#xff0c;大家好&#xff0c;我是兔哥。 之前写的SpringBoot日记本系统备受好评&#xff0c;考虑到还是有很多小伙伴不会部署&#xff0c;所以这一篇文章就单独来讲一下部署步骤吧。 需要资源 idea&#xff08;破不破解都行&#xff09; MySQL&#xff08;最好5.7以…

redis高级篇一

redis 是多线程还是单线程 redis单线程的操作 主要是指redis的网路IO和键值对的读写是由一个线程来完成的&#xff0c;Redis在处理客户端的请求时&#xff0c;包括获取&#xff08;socket 读&#xff09;&#xff0c;解析&#xff0c;执行&#xff0c;内容返回&#xff08;so…