目录
- 一、二叉树的链式结构
- 二、二叉树链式存储结构的实现
-
- [2.1 申请二叉树节点](#2.1 申请二叉树节点)
- [2.2 创建一个简单的二叉树](#2.2 创建一个简单的二叉树)
- [2.3 前序遍历](#2.3 前序遍历)
- [2.4 中序遍历](#2.4 中序遍历)
- [2.5 后序遍历](#2.5 后序遍历)
- [2.6 二叉树的节点个数](#2.6 二叉树的节点个数)
- [2.7 二叉树叶子节点个数](#2.7 二叉树叶子节点个数)
- [2.8 二叉树第K层节点个数](#2.8 二叉树第K层节点个数)
- [2.9 二叉树的深度或高度](#2.9 二叉树的深度或高度)
- [2.10 二叉树查找值为`x`的节点](#2.10 二叉树查找值为
x
的节点) - [2.11 层序遍历](#2.11 层序遍历)
- [2.12 二叉树的销毁](#2.12 二叉树的销毁)

【前言 】我们在之前的博客中介绍了树的种类 ,并且在前两期博客中实现了堆数据结构及堆的实际应用博客 ,这期博客我们来实现二叉树的链式存储 。没有注意到之前博客的小伙伴可以移步至我的个人主页自行查看。链接:个人主页
一、二叉树的链式结构
二叉树的链式存储结构 是指用链表来表示⼀棵二叉树,即用链表来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域 和左右指针域 ,左右指针分别用来给出该结点左孩子 和右孩子 所在的链结点的存储地址 。
如果没有左右孩子,那么对应的左右指针指向NULL
即可。
知道它的结构 之后我们就可以用代码呈现出来二叉树的存储结构。
c
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType val;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
我们依旧用结构体来实现二叉树的链式存储结构。
有了二叉树的存储结构,我们就可以对二叉树进行相关操作了,像前序遍历、中序遍历、后序遍历、层序遍历及计算结点个数
等等操作。下面我将要实现的操作呈现下来,让大家能够跟上博主的节奏,下面我会对它们进行详细讲解。
c
//申请节点
BTNode* BuyNode(BTDataType x);
//前序遍历------根左右
void preOrder(BTNode* root);
//中序遍历
void inOrder(BTNode* root);
//后序遍历
void postOrder(BTNode* root);
// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root);
// ⼆叉树叶⼦结点个数
int BinaryTreeLeafSize(BTNode* root);
// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root);
// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
//层序遍历
void leverOrder(BTNode* root);
// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);
二、二叉树链式存储结构的实现
2.1 申请二叉树节点
- 这里会用到动态内存申请函数
malloc
,并且会对申请的空间进行检查 - 将申请节点中储存的值赋值为目标值x
- 申请节点后将左右指针置为空
c
//申请节点
BTNode* BuyNode(BTDataType x)
{
BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
if (newNode == NULL)
{
perror("malloc fail!");
return NULL;
}
newNode->val = x;
newNode->left = newNode->right = NULL;
return newNode;
}
2.2 创建一个简单的二叉树
- 我们现在已经能够申请节点 了,为了完成
中序遍历
等遍历操作我们要创建一个二叉树; - 这里我们将申请
6
个节点创建一个二叉树。
c
BTNode* CreatTree()
{
BTNode* nodeA = BuyNode('A');
BTNode* nodeB = BuyNode('B');
BTNode* nodeC = BuyNode('C');
BTNode* nodeD = BuyNode('D');
BTNode* nodeE = BuyNode('E');
BTNode* nodeF = BuyNode('F');
nodeA->left = nodeB;
nodeA->right = nodeC;
nodeB->left = nodeD;
nodeB->right = nodeE;
nodeC->left = nodeF;
return nodeA;
}

从右侧内存中可以看出我们创建出了这样一棵二叉树。
2.3 前序遍历
前序遍历访问顺序为:根结点、左子树、右子树。
c
void preOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->val);
preOrder(root->left);
preOrder(root->right);
}
这就是前序遍历的递归代码 了,可能有很多人看完不能很理解,不要急,接下来,我来画图给大家分析。
另外这是我们的执行结果 :
上图就是详细的递归过程,最终我们的结果与程序执行的结果一样,大家可以停顿思考一下。
2.4 中序遍历
中序遍历访问顺序为:左子树、根结点、右子树。
c
void inOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
inOrder(root->left);
printf("%c ", root->val);
inOrder(root->right);
}
运行结果:
这里的递归过程和前序遍历基本类似,就不在呈现画图过程了。
2.5 后序遍历
后序遍历访问顺序为:左子树、右子树、根结点。
c
void postOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
postOrder(root->left);
postOrder(root->right);
printf("%c ", root->val);
}
运行结果:
2.6 二叉树的节点个数
二叉树的节点 = 左节点个数 + 右节点个数 + 根节点个数 。我们的代码依旧会采用递归 的方式实现,大家可以看一下代码是如何写的,代码写完后,我依旧会给大家画图解释。
c
//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return 1 + BinaryTreeSize(root->left)
+ BinaryTreeSize(root->right);
}
运行结果:
逐步画图:
这就是求二叉树节点个数的递归代码执行的过程。
2.7 二叉树叶子节点个数
二叉树的叶子节点个数等于左右孩子都为空的节点总和,所以在我们构建的树中,二叉树的叶子节点个数为3。
c
// ⼆叉树叶⼦结点个数
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);
}
这段代码和求二叉树的节点总数的代码有很多相似之处,此处就不再画图解释。
运行结果:
2.8 二叉树第K层节点个数
假设我们要求二叉树第二层节点个数 ,此时K=2
,我们用递归 实现的话,我们每递归一层相应的K
就要减一,直到K为1
时,说明到达了目标层数此时我们返回1
,证明找到第K
层的一个节点,将这样的节点数加起来即可 。下面让我们来看看递归代码怎么写。
c
// ⼆叉树第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);
}
为了大家能够更好的了解代码,我们进行画图分析。
运行结果:
这里我们求解的是第2层的节点个数,所以结果是2。
分析结果:
2.9 二叉树的深度或高度
二叉树的深度 = 1 + MAX(左子树的高度,右子树的高度)。下面让我们一起看看代码怎么写。
c
//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int leftDeep = BinaryTreeDepth(root->left);
int rightDeep = BinaryTreeDepth(root->right);
return 1 + (leftDeep > rightDeep ? leftDeep : rightDeep);
}
运行结果:
2.10 二叉树查找值为x
的节点
二叉树查找值为x
的节点就比较好写了,只要递归 发现节点的值为x
就返回节点即可,我们一起来看一下递归代码长什么样。
c
// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->val == x)
{
return root;
}
BTNode* leftfind = BinaryTreeFind(root->left, x);
if (leftfind)
{
return leftfind;
}
BTNode* rightfind = BinaryTreeFind(root->right, x);
if (rightfind)
{
return rightfind;
}
return NULL;
}
运行测试:
2.11 层序遍历
二叉树的层序遍历 用到了【数据结构】队列 ,由于我们之前已经实现了队列这一数据结构,所以我们将会把队列代码拷贝过来,队列博客,详情请点击~
代码实现:
c
//层序遍历
void leverOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
//去队头,出队头
BTNode* pcur = QueueFront(&q);
QueuePop(&q);
printf("%c ", pcur->val);
if (pcur->left)
{
QueuePush(&q, pcur->left);
}
if (pcur->right)
{
QueuePush(&q, pcur->right);
}
}
QueueDestroy(&q);
}
代码中 ,Queue
是建立队列的封装函数,QueueInit
是初始化队列的封装函数,QueueEmpty
是判断队列是否为空的封装函数,QueueFront
是取队头的封装函数,QueuePush
是入队列的封装函数,QueuePop
是出队头的封装函数。
运行结果:
过程详解:
2.12 二叉树的销毁
二叉树的销毁 过程中,我们已知根节点 ,所以我们首先要递归到最下面 ,然后从下往上依次销毁,也就是说要先销毁左右子树,不然你先销毁根节点就找不到左右子树了。
c
//二叉树的销毁
void BinaryTreeDestory(BTNode** root)
{
if ((*root) == NULL)
{
return;
}
BinaryTreeDestory(&((*root)->left));
BinaryTreeDestory(&((*root)->right));
free(*root);
*root = NULL;
}
执行结果:
从右侧内存中可以看出,二叉树已经完全销毁了。
总结:
以上就是本期博客分享的全部内容啦!如果觉得文章还不错的话可以三连支持一下,你的支持就是我前进最大的动力!
技术的探索永无止境! 道阻且长,行则将至!后续我会给大家带来更多优质博客内容,欢迎关注我的CSDN账号,我们一同成长!
(~ ̄▽ ̄)~