数据结构之入门二叉树

news2024/11/13 7:53:04

文章目录

  • 前言
  • 1.二叉树的知识铺垫
  • 2.二叉树的具体实现
    • 1.递归实现前中后序遍历
    • 2.其它相关接口的实现
      • 1.求二叉树的节点个数
      • 2.求叶子节点个数
      • 3.二叉树查找值为x的节点
      • 3.求树高度
      • 4.求k层节点的个数
      • 5.层序遍历
      • 6.判断二叉树是否是完全二插树
  • 3.总结

前言

之前用数组实现了一种特殊的完全二叉树——堆。本文将通过链式的方式实现二叉树。具体的实现方式是通过递归,在之前的文章中就提到过二叉树的结构的递归定义的。本文开始会介绍一些相关概念,之后会介绍一些二叉树的接口实现,关于二叉树的构建,浅介绍一下,以后会具体展开细讲,本文主要的目的是入门二叉树。


1.二叉树的知识铺垫

之前介绍过树的相关知识铺垫,这里针对二叉树再介绍一些相关的概念。二叉树的具体实现主要是通过递归方式,因为二叉树本身就是递归定义的,通过递归实现起来比较容易也比较容易理解。这也算是入门二叉树最好的方式了。这里先介绍二叉树的3种遍历方式,前序遍历,中序遍历,后序遍历。

前序遍历,遍历顺序是根 左子树 右子树。先遍历二插树的根 ,再遍历左子树,再遍历右子树。当遍历到左子树的时候,再从 根 左子树 右子树的顺序遍历,一直从这种嵌套的方式往下走,直到遇见空树再往回走。然后再用同样的方式遍历右子树,这样讲可能比较抽象,我们画图分析。

在这里插入图片描述
在这里插入图片描述
这个二叉树要先将一个完整的大树看成由多个子树构成,这些子树又可以单独看作颗颗小树。这样去看待二叉树比较容易理解。

中序遍历的顺序是 左子树 根 右子树。我们还是画图来遍历更加直观。

在这里插入图片描述
在这里插入图片描述

后序遍历顺序是左子树 右子树 根,还是画图分析。

在这里插入图片描述
对这个二叉树的理解最好不要从整体来看,要学会将这整个树看成一颗颗单独的小树。前序遍历因为一开始就是根所以看起来可能有点自上向下的味道,而中序和后序不是从根节点开始,可能有种从底向上的感觉。但是不管怎样,对这3种遍历方式的顺序要牢记。

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。


2.二叉树的具体实现

1.递归实现前中后序遍历

了解了二叉树的前中后序遍历后,我们就要去具体实现。之前提到了要用递归来实现二叉树的相关接口,所以前中后序遍历也是采用递归。

在实现遍历之前,我们先对二叉树节点结构定义声明

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

接着就是遍历具体实现了,采用递归实现
前序遍历代码示例

void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	printf("%c ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

我们好好分析一下这个递归过程,分析好这个后,后面的递归酌情分析了

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

以上就先序遍历的整个递归过程,这个过程看似复杂,但是实际上理清逻辑后还是很简单的。

中序遍历代码示例

void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%c ", root->data);
	BinaryTreeInOrder(root->right);
}

后序遍历代码示例

void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->data); 
}

通过上述3种遍历方式的代码,可以发现这样一个特点,3种遍历方式的代码只是打印代码位置或者说顺序改变了,其余都是相同的。这个打印代码就可以简单的理解为遍历,先序遍历是根先遍历,所以判断根不为空后就直接打印,中序遍历是先遍历左子树,接着是根,所以打印根节点是中间,后序遍历是最后遍历根,所以根节点的打印放再后面。

到了二叉树这里,首先要理解的是递归,递归这种操作不要过多的关注递归每一步做了什么,不要过多关注内部的细节,程序反复调用自身即是递归。既然递归是一个反复调用自身的过程,这就说明它每一级的功能都是一样的,因此我们只需要关注一级递归的解决过程即可。

用递归解决问题,首先明确以下两点:1.递归中止条件,也就是递归再什么时候结束。2.确定递归的返回值,也就是递归应该干什么。在二叉树中,不要把树看作有多节点的树,应该把树看作root(根),left(左子树),right(右子树),这3部分组成,回到这个遍历,当root节点是空时,就没必要遍历打印了直接返回,这是空树。如果不等与空,那么就是先打印根节点,然后再调用这个函数去打印left左子树,接着就是打印right右子树。同理,中序遍历,是先打印左子树,那么就是直接调用这个函数传入left作为参数,然后打印根节点,再调用这个函数传入right打印右子树;后序遍历是先打印左子树,所以先调用这个函数传入左子树left,然后是打印右子树调用这个函数传入右子树right,最后是打印root根节点。只要理清这点递归就很好写了。

在这里插入图片描述

同时还有个蠢方法,如果遇到需要用先中后序某一种顺序解决问题的话,记住先序就是先写操作再递归,中序就是先递归再写操作再递归,后序就是先递归,最后写操作。

在这里插入图片描述
不过还是推荐理解递归处理的思路,理清逻辑解决问题


2.其它相关接口的实现

1.求二叉树的节点个数

递归实现求节点个数,首先明确递归结束条件,当根节点为空时,就是空树,返回0即可。然后就是递归了,这个递归还是把树看作3部分组成,根算一个节点,然后左右子树各自节点个数,最后将这3者相加起来就是所求的节点个数

代码示例

//求二叉树节点的个数
int BinaryTreeSize(BTNode* root)
{  //如果根节点为空就是空树节点为0
   //如果根节点不为空就就是左子树和右子树的节点数加1,这个1代表的是根节点

	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left)+1+BinaryTreeSize(root->right);
}

梳理清楚后这个递归就很好写了,这个函数本身的功能就是用求树的节点个数,将左右子树也单独剥离看作成一颗树,所以递归的参数的就直接是左右子树最后加上根这个节点,即为所求。


2.求叶子节点个数

求叶子节点采用递归,递归的边界条件就是考虑当只有这颗树只有根节点时出现的所有可能出现的情况,如此一来边界条件就很好写了,当根节点为空时,叶子节点肯定是0,除此之外还有一种可能根节点本身就是叶子节点,这个时候根节点的左右子树肯定都为空,叶子节点就为1。边界条件由此确定好了,接着就是递归调用了,递归求左右子树中的叶子节点,将左右子树的叶子节点求出相加即可。

代码示例

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left==NULL && root->right==NULL)
	{
		return 1;
	}
	int n1=BinaryTreeLeafSize(root->left);
	int n2=BinaryTreeLeafSize(root->right);
	return n2 + n1;
}

在代码中,我用了n1和n2接收这个左右子树的叶子节点个数,不用n1和n2和接收,直接返回递归相加也是可以的。但是这样写的话肯定递归嵌套更深一点。大致分析这个递归过程就知道不用变量接收会第一次返回时不会记住这个值,会多返回一次。可以仿照上面画一画递归展开图就知道了。


3.二叉树查找值为x的节点

采用递归的方式来进行查找,递归的边界判断是如果根节点是空表示空树,直接返回空。如果根节点的data是要查找的值x,那么就返回这个节点。如果根查找完没有找到这个值,就接着查找这颗树的左右子树。对左右树进行查找这个过程就是递归,这个函数的功能就是查找,由此传入左右子树当作参数,再接收这个函数返回值进行判断,如果不是空,就返回这个接收值。最后如果这颗树没有这个值x就直接返回空

代码示例

//二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
	{
		return ret1;
	}
	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
	{
		return ret2;
	}
	return NULL;
}

递归解决二叉树相关问题,应该把二叉树看作根 左子树 右子树 这3部分组成。递归边界条件就是考虑到只有一个根节点所有情况的处理。其次,这个函数本身的功能要确定,也就是这个函数返回值的意义,这个返回值的意义又取决于边界条件的处理,以上述代码为例,这个函数本身就用来查找节点的data值的,如果根节点没有查找到,那么势必会对左右子树进行查找。那么就再次调用这个函数进行查找,到了这里递归就出现了。用变量来接收递归的查找的结果,如果不是空就查找到了,直接返回即可。如果整个递归结束了也没有找到,那么最后返回也返回空。代码中对左右子树的if判断取决于边界处理,边界处理的时候就可能返回两种情况,一种是找到了返回节点,另一种就是没找到返回空。所以这个if判断递归查找的情况。


3.求树高度

求树的高度采用递归,首先按照之前递归的经验,先考虑边界条件也就是只有根节点的情况,当根节点为空时,这颗树的高度就为0,这个边界条件确定了。接着就是对左右子树的处理,求左右子树的高度,选取左右子树较高的那一个值,最后加上根节点的高度,也就是加上1。这个结果就是树的高度,之所以要选取左右子树两者中较大的一个,这颗树可能不是满二叉树,左右子树的高度可能不一致。

代码示例

int BinaryTreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int left_height = BinaryTreeHeight(root->left);
	int right_height = BinaryTreeHeight(root->right);
	return left_height > right_height ? left_height + 1 : right_height + 1;
}

用变量来接收递归结果的返回值,选取较大的结果加1,处理了这么多的递归,其次我们发现这个递归调用主要发生在左右子树上,这个返回值的意义又是由边界条件确定的。所以在处理递归的时候,不要老是纠结递归每一步干了什么,要明确这个函数返回值的意义。这个返回值的意义又绕回到边界处理,这一切都是相辅相成的,通过函数功能设置合理的返回值。


4.求k层节点的个数

求k层节点的个数,还是采用递归。首先处理边界条件,当根为空时表示空树节点数为0,当求第一层时,只有一个节点就是根节点。接着处理左右子树,到了这一步就开始递归了,如果整颗树是k层,那么对于左右子树来说它们就在k-1层。因为我们将这颗树看作由根 左右子树 这3部分组成,所以对于这颗树来说,左右子树它们在k-1层,它们上面只有一层(根节点)。这样的话,递归参数的就确定下来了。

//求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);
}

递归参数的左右子树比较容易确定,这个k-1可能需要思考一会,但是这些还是逃不过之前的分析,就是把树拆成3部分来看。如果根是第一层,那么左右子树就是k-1层,当然也可以像之前那样用变量来接收递归这个返回值,最后相加返回也是可以的。边界条件的处理k==1算是一个隐藏的情况,k==1对应着也是对根的处理,因为第一层就是根节点,这点需要考虑周全。


5.层序遍历

之前介绍了二叉树的3种遍历方式,前中后序遍历。这3种遍历方式处理二叉的时候比较常见,还有一种不常见的遍历,也就是层序遍历。层序遍历就是像数组那样按顺序遍历,也就是对二叉树一层一层的遍历。

在这里插入图片描述

我们这里的二叉树都是以链式的形式的表示的,不像堆那样采用数组存储,这种遍历方式对链式二叉树来说还是有点棘手的。由此我们借用另一种数据结构队列来辅助处理,我们将二叉树的结构一层一层的遍历。大致思路就是将二叉树的每一层的节点插入队列中,再依次一层一层的出队头的数据,每次出一个数据就将这个数据从队列中pop掉,直到将二叉树遍历完。

在这里插入图片描述

代码示例

//层序遍历 
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q,root);
	}
	while (!QueueEmpty(&q))
	{  
			BTNode* front = QueueFront(&q);
			printf("%d ", front->data);
			QueuePop(&q);
			if (front->left)
			{
				QueuePush(&q,front->left);
			}
			if (front->right)
			{
				QueuePush(&q, front->right);
			}
	}

	printf("\n");
	QueueDestroy(&q);
}

上述代码的过程如下图
在这里插入图片描述

最先插入根节点,然后就是遍历,遍历完出队列头节点也就是出这个根节点,再依次插入头节点front的左右子树,再出头节点,直到队列为空。这个pop作用就是原头节点遍历完后,更新新的头节点。这个插入的过程其实并不是一层层将节点插入队列中,而是按照层序的顺序一带二,一个节点带入该节点的左右子树,层序遍历就是按照排队的顺序从前往后走,队列这种数据结构刚好符合这样的特性,先进先出。所以用队列实现层序遍历是特别合适。同时代码的if判断相当于筛选出节点的空子树,哪怕不是完全二叉树也可以遍历完所有节点。

注意上述代码中队列接口函数需要自己实现,因为C语言中不提供这样的接口,关于队列的接口实现在之前的博客中也讲解介绍。


6.判断二叉树是否是完全二插树

判断二叉树是否是完全二叉树,就要用到之前介绍的层序遍历。当我们用队列出队头数据时,一旦出到了空节点,就不再往队列中插入数据,如果这时队列不为空,就继续出数据,如果一旦出了非空节点说明这颗树就不是完全二叉树。

在这里插入图片描述
代码示例

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{   
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

从图中的分析和代码示例中可以看到实际上空节点也是进入队列了,空节点很重要,空节点即是不再往队列中进数据的重要依据也判断是否为完全二叉树的重要凭证,这个front临时变量也很重要,每次pop掉队列头节点后,再用这个变量将头节点的左右子树插入队列中。本段代码核心就是层序遍历的方式遍历节点以及对树中空节点的处理。

同时,这段代码中的队列接口也需要自己实现


3.总结

  • 1.入门二叉树常用的方式就是递归,递归不要纠结于每一层干了什么,搞清楚一层干了什么。实际上就是确定递归边界条件,同时根据函数功能确定好递归函数的返回值。
  • 2.递归处理二插树时,可以把二插树看作3部分组成,根节点,根的左右子树。对根节点的处理就是递归边界条件的处理,左右子树实际上是重复这样的处理。
  • 3.以上内容如有错误,欢迎指正!

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

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

相关文章

解决word写论文改成两栏时mathtype的公式无法居中的问题

想要的格式 投论文的时候,有很多期刊的要求是两栏排版,比如中国电机工程学报 理论上,用mathtype输入的公式,会根据word的格式自动地调整自己的缩进量(更确切地说,是会自动调整制表位)。比如下…

新手入门指南之玩转蓝桥云课(线上运行虚拟机,c++,Java,Javaweb,python环境,以及如何成功利用命令行运行这些环境)

目录 你好,蓝桥云课 👋 🌲欢迎来到蓝桥云课🌲 为什么要用 Linux 实验精神 蓝桥云课课程 加入课程 开始实验 实验环境 讨论发帖 实验报告 有效学习时间 我们的目标是星辰大海 🍐Linux 桌面环境使用指南&…

【C++ Primer】阅读笔记(4):string

目录 简介初始化string对象初始化方法直接初始化与拷贝初始化string对象上的操作操作方式读取string对象(cin>>s、cout<<s)getline(is,s)empty()size()string对象的比较(>、<、<=、>=)两个string对象相加(s1+s2)字面值与string对象相加遍历string对象参…

Jenkins复制和导出导入job

一、同一个Jenkins中复制job如果是同一个Jenkins复制job&#xff0c;只需要在创建Job时&#xff0c;选择Copy from一个已有的job即可。也就是在下方上面的红框中输入你新建job的名称&#xff0c;在下面的红框中输入你想要复制的job(已经建好的job)&#xff0c;然后点击确定。这…

【C初阶】实用调试技巧

实用调试技巧前言一、什么是bug&#xff1f;二、调试是什么&#xff1f;&#xff08;一&#xff09;调试&#xff08;二&#xff09;调试的基本步骤&#xff08;三&#xff09;Debug和Release的介绍。1.简单介绍三、Windows环境调试介绍&#xff08;一&#xff09;调试环境的准…

【SAP Abap】SAP ALV开发(REUSE_ALV_GRID_DISPLAY_LVC)全网最详细 DEMO

SAP ALV开发&#xff08;REUSE_ALV_GRID_DISPLAY_LVC&#xff09;全网最详细 DEMO1、ALV 简介2、演示程序1、ALV 简介 ALV 报表程序&#xff0c;是我们在 SAP 项目中最常开发的程序类型。 SAP 系统提供了三种 ALV 实现方式&#xff0c;分别是&#xff1a; FM ALV&#xff0c;…

win10下安装多个JDK配置方法

环境变量配置 此电脑 --- 属性 -- 高级系统设置 -- 环境变量 -- 系统变量 -- 新建 1、配置JAVA_HOME JAVA6_HOME jdk1.6的安装路径 JAVA8_HOME jdk1.8的安装路径 JAVA_HOME %JAVA8_HOME% 两个%表示引用变量的值&#xff0c;直接用JAVA_HOME&#xff0c;就能表示一连串长…

SAR影像变化检测方法之(SAR_CD_DDNet(无监督变化检测))论文阅读

一、论文信息 1、论文标题&#xff1a;Change Detection in Synthetic Aperture Radar Images Using a Dual-Domain Network 2、代码链接&#xff1a;https://github.com/summitgao/SAR_CD_DDNet 二、摘要 合成孔径雷达(SAR)图像的变化检测是一项关键而具有挑战性的任务。现…

Android---BottomAppBar

目录 BottomAppBar FloatingActionButton UI 完整代码 BottomAppBar Material Design 的一个重要特征是设计 BottomAppBar 。可适应用户不断变化的需求和行为。因此&#xff0c;BottomAppBar 是一个从标准物质指导的演变。它更注重功能&#xff0c;增加参与…

ansible 第三天

1.挂载本地光盘到/mnt 2.配置yum源仓库文件通过多种方式实现 仓库1 &#xff1a; Name: RH294_Base Description&#xff1a; RH294 base software Base urt: file:///mnt/BaseOS 不需要验证钦件包 GPG 签名 启用此软件仓库 仓库 2: Name: RH294_Stream Description &#xff1…

Android Studio 用WIFI无线调试adb (3种方法)

最近数据线一直被boss借走&#xff0c;一旦借走就无法调试了&#xff0c;所以就打算不用数据线调试了&#xff0c;使用WIFI进行调试。 一、命令 1、首先配置adb环境变量 首先找到andoridSDK的路径中的platform-tools文件夹 &#xff0c;将他的路径复制我的是 C:\Users\YiShi…

365天深度学习训练营-第P7周:YOLOv5-Backbone/YOLOv5-C3模块实现

目录 一、前言 二、我的环境 三、代码实现 1、C3模块 2、Backbone模块 一、前言 >- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/xLjALoOD8HPZcH563En8bQ) 中的学习记录博客** >- **&#x1f366; 参考文章&#xff1a;36…

【算法】day03:动态规划dp

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录1. 青蛙跳台阶2. 矩形覆盖3. 二进制中1的个数4. 链表中倒数第k个结点普通小孩也要热爱生活&#xff01; 1. 青蛙跳台阶 跳台阶 1&#xff09;思路&#xff1a;如果青蛙在第n个台阶上&#xff0c;那么它要么来自n-1…

python命名规范

1.命名有字母、数字和下划线三个组成 2.项目名、包名、模块名、python文件名 都应该用英文开头&#xff0c;不能纯数字&#xff0c;否则无法识别 3.脚本名不能跟系统模块或三方模块重复 4.函数、类、方法、参数名称不能跟系统现有名称重复 5.类名必须以大写字母开头、除了类…

Kafka在windows下下载、启动、测试详细教程

目录 下载地址 启动 启动zookeeper 启动kafka 队列操作 创建消息队列名 删除消息队列名 查看所有的队列 测试 生产测试 消费测试 下载地址 Apache KafkaApache Kafka: A Distributed Streaming Platform.https://kafka.apache.org/downloadswindows下kafka3.0版本的…

G120系列变频器进行参数备份(上传或下载)的2种基本方法介绍(Startdrive+SD卡)

G120系列变频器进行参数备份(上传或下载)的2种基本方法介绍(Startdrive+SD卡) 1. 通过SINAMICS SD存储卡对G120系列变频器进行参数上传/下载的具体方法 适配范围:CU240B-2(DP)/CU240E-2(DP/PN)/CU250S-2(DP/PN) 可将变频器的参数设置保存至 SINAMICS SD 卡。在进行变频器更…

回归预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络多输入多输出

回归预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络多输入多输出 目录回归预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络多输入多输出预测效果基本介绍程序设计往期精彩参考资料预测效果 基本介绍 MATLAB实现CNN-LSTM卷积长短期记忆神经网络多输入多输出&#xff0c;运行…

docker swarm 集群部署和验证

docker swarm可以轻松的部署集群,实现负载均衡,服务器的扩容和扩容的反义词. 首先docker的官方文档非常的不稳定,梯子也不解决问题.还好有替代 docs.docker.com 用不了 https://docs.docker.com.zh.xy2401.com/engine/swarm/swarm-tutorial/ 很好 场景: 三台机器 x.x.x.104 x.…

经典文献阅读之--FAST-LIVO(Fast-LIO系列的激光视觉惯性里程计)

0. 简介 在此之前博主对Fast-LIO2和R3LIVE进行了系统性的学习。最近文章《Fast and Tightly-coupled Sparse-Direct LiDAR-Inertial-Visual Odometry》提出了FAST-LIVO&#xff0c;这是一种快速LiDAR惯性-视觉里程计系统&#xff0c;它建立在两个紧耦合的直接里程计子系统之上…

结构体、枚举、联合(14)

目录 1、结构体 1、结构的声明 2、特殊的声明 2、结构的自引用 3、结构体变量的定义和初始化 4、结构体内存对齐 1、结构体的对齐规则&#xff1a; 2、为什么存在内存对齐? 3、修改默认对齐数 5、结构体传参 2、枚举 1、枚举类型的定义 2、使用 3、优点 3、联合…