

目录
[2.1 前序遍历](#2.1 前序遍历)
[2.1.1 代码实现](#2.1.1 代码实现)
[2.2 中序遍历](#2.2 中序遍历)
[2.2.1 代码实现](#2.2.1 代码实现)
[2.3 后序遍历](#2.3 后序遍历)
[2.3.1 代码实现](#2.3.1 代码实现)
[3.1 代码实现](#3.1 代码实现)
[4.1 计算节点总个数](#4.1 计算节点总个数)
[4.2 计算叶子节点的个数](#4.2 计算叶子节点的个数)
[4.3 计算树的高度](#4.3 计算树的高度)
[4.5 计算第k层节点的个数](#4.5 计算第k层节点的个数)
[4.6 判断二叉树是否是完全二叉树](#4.6 判断二叉树是否是完全二叉树)

一、节点设计
cpp
typedef int BTreeDatatype;
typedef struct BinaryTreeNode
{
BTreeDatatype _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
BTreeDatatype这是一个类型别名,把 int 重命名为 BTreeDatatype。
好处是后续如果想把节点存储的数据类型改成 char 或 double,只需要修改这一行,不需要改动整个代码里的 int。
struct BinaryTreeNode:这是结构体的标签名,用来在结构体内部定义指向自身的指针。
_data:存储节点的有效数据,类型是我们上面定义的BTreeDatatype(也就是int)。_left:指向当前节点的左子节点的指针,类型是struct BinaryTreeNode*。如果没有左子节点,这个指针为NULL。_right:指向当前节点的右子节点的指针,类型是struct BinaryTreeNode*。如果没有右子节点,这个指针为NULL。
typedef ... BTNode:把整个 struct BinaryTreeNode 重命名为 BTNode,后续创建节点时可以直接用 BTNode 代替 struct BinaryTreeNode,简化代码。
二、深度优先遍历(前,中,后)
二叉树的遍历,标准专业定义是:按照某种有规律、无重复、无遗漏的顺序,访问二叉树中的每一个节点,并且每个节点仅被访问一次的过程。
遍历的最终目的是获取二叉树中所有节点的数据,或者对每个节点执行特定操作(比如修改节点值、统计节点个数、查找目标节点等)------ 没有遍历,我们就无法高效地操作二叉树中的批量节点。
二叉树的遍历分为两大体系深度优先遍历和广度优先遍历,这是所有遍历方式的基础。下面我们首先来学习一下深度优先遍历:深度优先遍历优先 往深走,沿着一条路径走到叶子节点(,再回溯返回,继续探索下一条路径。主要细分为三种方式:前序遍历、中序遍历、和后序遍历。这里的前中后主要说明二叉树的根节点或者子树的根节点的访问顺序,比如我们举个例子:

如图,这就是一个最简单的二叉树结构,根据深度优先遍历它的前、中、后序遍历的结果可以如下表示:
- 前序遍历(根,左,右):8,5,7
- 中序遍历(左,根,右):5,8,7
- 后序遍历(左,右,根):5,7,8
但是,在实际情况下二叉树的结构会非常复杂,下面我们来详细讨论一下三种深度优先遍历的流程与代码实现:
2.1 前序遍历
在复杂的二叉树结构之中,核心要抓住前序遍历根 → 左子树 → 右子树 的核心规则,哪怕二叉树结构再复杂,也可以拆分成单个节点 + 左右子树的最小单元来执行
- 先访问当前节点(根节点,这里的根是相对概念,既可以是整棵树的根,也可以是任意子树的根)
- 再递归前序遍历当前节点的左子树(把左子树当成一棵独立的二叉树,重复前序遍历规则)
- 最后递归前序遍历当前节点的右子树(把右子树当成一棵独立的二叉树,重复前序遍历规则)
通俗点说:遇到一个节点先 处理 它,再把左子树啃透,最后再啃右子树,哪怕左 / 右子树嵌套再深,也遵循这个 "先自己、再左、后右" 的逻辑。
如图,我们将前面得二叉树得结构再复杂一点,它得前序遍历流程如下:

1.访问到8节点,首先打印8然后访问其左子树。
2.访问到左子树5节点,首先打印5然后访问其左子树。
3.访问到左子树9节点,首先打印9然后访问其左子树。
4.此时9的左子树不存在然后访问9的右子树发现右子树也不存在,此时对于9这个节点来讲它的前序遍历(根左右)完成回退到上一层5节点访问其右子树。
5.访问到右子树3节点,首先打印3然后访问其左子树。
6.此时3的左子树不存在然后访问3的右子树发现右子树也不存在,此时对于3这个节点来讲它的前序遍历(根左右)完成回退到上一层5节点,此时对于5这个节点来讲它的前序遍历(根左右)完成回退到上一层8节点访问其右子树。
7.访问到右子树7节点,首先打印7然后访问其左子树。
8.访问到左子树4节点,首先打印4然后访问其左子树。
9.此时4的左子树不存在然后访问4的右子树发现右子树也不存在,此时对于4这个节点来讲它的前序遍历(根左右)完成回退到上一层7节点访问其右子树。
10.访问到右子树6节点,首先打印6然后访问其左子树。
11.此时6的左子树不存在然后访问6的右子树发现右子树也不存在,此时对于6这个节点来讲它的前序遍历(根左右)完成回退到上一层7节点,此时对于7这个节点来讲它的前序遍历(根左右)完成回退到上一层8节点,8节点的前序遍历完成遍历结束。
前序遍历结果:8,5,9,3,7,4,6
2.1.1 代码实现
递归版本实现:
cpp
void Preorder(BTNode* phead)
{
if (phead == NULL)
{
return;
}
printf("%d ",phead->_data);
Preorder(phead->_left);
Preorder(phead->_right);
}
这段代码实现了二叉树的前序遍历(Preorder Traversal)。
执行流程
-
终止条件:如果当前节点指针
phead为NULL(空节点)返回。 -
访问当前节点:打印当前节点存储的数据
phead->_data。 -
递归遍历左子树:调用
Preorder(phead->_left)处理左孩子。 -
递归遍历右子树:调用
Preorder(phead->_right)处理右孩子。
非递归版本实现:
如果要将前序遍历的递归版本的代码改为非递归,我们可以用栈来模拟程序栈的压栈出栈操作。
cpp
void PreorderNonR(BTNode* phead)
{
assert(phead);
ST st;
Init(&st);
//将根节点入栈
ST_push(&st, phead);
while (!STEmpty(&st))
{
BTNode* terget = ST_top(&st);//取栈顶的元素
ST_pop(&st);
printf("%d ",terget->_data);
if (terget->_right != NULL)
{
ST_push(&st, terget->_right);
}
if (terget->_left != NULL)
{
ST_push(&st, terget->_left);
}
}
Destory(&st);
}
用 assert(phead) 确保传入的根节点非空,随后初始化栈并将根节点入栈。
当栈不为空时,重复以下操作:
- 出栈并访问:取出栈顶节点,弹出后打印其数据(完成节点的访问)。
- 右孩子入栈:若当前节点有右孩子,先将右孩子入栈(栈后进先出,保证左孩子先被处理)。
- 左孩子入栈:若当前节点有左孩子,再将左孩子入栈(下一轮循环会优先处理左孩子,完成「左」的遍历)。
遍历结束后调用 Destory(&st) 销毁栈,避免内存泄漏。

栈结构的模拟实现请参考本作者下面的博客:

2.2 中序遍历
与前序遍历类似,中序遍历核心要抓住前序遍历左子树 → 根 → 右子树 的核心规则
- 先递归中序遍历当前节点的左子树(把左子树当成一棵独立的二叉树,重复中序遍历规则)
- 再访问当前节点(根节点,这里的根是相对概念,既可以是整棵树的根,也可以是任意子树的根)
- 最后递归中序遍历当前节点的右子树(把右子树当成一棵独立的二叉树,重复中序遍历规则)

1.访问到8节点,首先访问其左子树。
2.访问到左子树5节点,访问其左子树。
3.访问到左子树9节点,访问其左子树。
4.此时9的左子树不存在然后回退到该子树的9节点并打印9,然后访问右子树发现右子树也不存在,此时对于9这个节点来讲它的中序遍历(左根右)完成回退到上一层5节点。
5.5节点左子树遍历完毕,打印5然后访问到右子树3节点。
6.此时3的左子树不存在然后打印根节点3,访问3的右子树发现右子树也不存在,此时对于3这个节点来讲它的中序遍历(左根右)完成回退到上一层5节点,此时对于5这个节点来讲它的中序遍历(左根右)完成回退到上一层8节点打印8然后访问其右子树。
7.访问到右子树7节点,访问其左子树。
8.访问到左子树4节点,访问其左子树。
9.此时4的左子树不存在然后打印4,访问4的右子树发现右子树也不存在,此时对于4这个节点来讲它的中序遍历(左根右)完成回退到上一层7节点打印7然后访问其右子树。
10.访问到右子树6节点,访问其左子树。
11.此时6的左子树不存在然后打印6,访问6的右子树发现右子树也不存在,此时对于6这个节点来讲它的中序遍历(左根右)完成回退到上一层7节点,此时对于7这个节点来讲它的中序遍历(左根右)完成回退到上一层8节点,8节点的中序遍历完成遍历结束。
中序遍历结果:9,5,3,8,4,7,6
2.2.1 代码实现
递归版本:
cpp
void Inorder(BTNode* phead)
{
if (phead == NULL)
{
return;
}
Inorder(phead->_left);
printf("%d ", phead->_data);
Inorder(phead->_right);
}
这段代码实现了二叉树的中序遍历(Inorder Traversal)
执行流程
-
终止条件:如果当前节点指针
phead为NULL(空节点)返回。 -
递归遍历左子树:调用
Preorder(phead->_left)处理左孩子。 -
访问当前节点:打印当前节点存储的数据
phead->_data。 -
递归遍历右子树:调用
Preorder(phead->_right)处理右孩子。
非递归版本:
cpp
//非递归中序遍历
void InorderNonR(BTNode* phead)
{
assert(phead);
ST st;
Init(&st);
BTNode* cur = phead;
while (cur!=NULL||!STEmpty(&st))
{
//从该非空节点起遍历到最左侧沿途全入栈
while (cur)
{
ST_push(&st, cur);
cur = cur->_left;
}
BTNode* target = ST_top(&st);//取栈顶的元素
ST_pop(&st);
printf("%d ",target->_data);
cur = target->_right;
}
Destory(&st);
}
二叉树中序遍历(左 - 根 - 右)非递归算法,核心是用自定义栈替代递归的隐式栈,完成节点的遍历、暂存和访问,最终打印出所有节点的值,遍历结束后还会销毁栈以释放内存,避免内存泄漏。
1.前置准备
cpp
assert(phead);
ST st;
Init(&st);
BTNode* cur = phead;
assert(phead):这是一个断言判断,目的是防止传入空的二叉树根节点导致后续空指针操作(如果phead为NULL,程序会直接终止并提示错误位置)。ST st; Init(&st);:定义一个自定义栈st,并调用Init函数初始化栈(通常是将栈顶指针置为-1,标识栈为空)。BTNode* cur = phead;:定义当前遍历指针cur,初始指向二叉树根节点,后续用它来遍历整个二叉树。
2.外层循环(控制整体遍历流程)
cpp
while (cur!=NULL||!STEmpty(&st))
循环条件是当前指针cur非空或 栈st不为空,这两个条件满足其一就继续遍历,原因是:
cur != NULL:表示还有未压入栈的节点(需要继续找最左侧节点)。!STEmpty(&st):表示栈中还有暂存的节点(需要弹出访问并处理其右子树)。只有当两个条件都不满足时,才说明所有节点都已遍历完毕,退出循环。
3.内层循环(找最左侧节点并压栈)
cpp
//从该非空节点起遍历到最左侧沿途全入栈
while (cur)
{
ST_push(&st, cur);
cur = cur->_left;
}
这是一个嵌套循环,目的是找到当前子树的最左侧节点,并将沿途所有节点依次压入栈:
while (cur):等价于while (cur != NULL),只要cur非空就继续循环。ST_push(&st, cur):将当前节点cur压入栈中(暂存,后续需要回溯访问它)。cur = cur->left:将cur指向当前节点的左子节点,继续向左深入,直到cur为NULL(此时已经到达当前子树的最左侧,没有更多左子节点了)。
4.弹出栈顶节点并访问
cpp
BTNode* target = ST_top(&st);//取栈顶的元素
ST_pop(&st);
printf("%d ",target->_data);
cur = target->_right;
当内层循环结束后,cur为NULL,此时栈顶就是当前子树的最左侧节点,需要弹出并访问:
BTNode* target = ST_top(&st);:调用ST_top函数获取栈顶节点(不弹出,只是读取),赋值给target。ST_pop(&st);:调用ST_pop函数弹出栈顶节点(此时栈顶指针下移,该节点不再保存在栈中)。printf("%d ", target->_data);:打印target节点的值,即访问该节点(这是中序遍历的「根」节点访问步骤,对应左 - 根 - 右中的「根」)。
5.转向右子树(继续遍历)
cpp
cur = target->right;
将当前指针cur指向刚刚访问过的target节点的右子节点,目的是继续遍历该节点的右子树:
- 此时
cur被赋值为target->right,如果右子节点非空,外层循环会再次进入内层循环,找右子树的最左侧节点并压栈。 - 如果右子节点为空,外层循环会继续弹出栈中的下一个节点,继续回溯处理。
6.销毁栈(释放资源)
cpp
Destory(&st);
遍历结束后,调用Destory函数销毁栈,目的是释放栈占用的资源。

2.3 后序遍历
中序遍历核心要抓住前序遍历左子树→ 右子树→ 根 的核心规则
- 先递归后序遍历当前节点的左子树(把左子树当成一棵独立的二叉树,重复后序遍历规则)
- 再递归后序遍历当前节点的右子树(把右子树当成一棵独立的二叉树,重复后序遍历规则)
- 最后访问当前节点(根节点,这里的根是相对概念,既可以是整棵树的根,也可以是任意子树的根)

访问到 8 节点,首先访问其左子树。
访问到左子树 5 节点,访问其左子树。
访问到左子树 9 节点,访问其左子树。
此时 9 的左子树不存在,访问 9 的右子树,发现右子树也不存在。此时对于 9 这个节点来讲它的后序遍历(左 - 右 - 根)完成,打印 9,回退到上一层 5 节点。
5 节点左子树遍历完毕,访问其右子树 3 节点。
访问到右子树 3 节点,访问其左子树,发现左子树不存在,访问 3 的右子树,发现右子树也不存在。此时对于 3 这个节点来讲它的后序遍历(左 - 右 - 根)完成,打印 3,回退到上一层 5 节点。
5 节点右子树遍历完毕,此时对于 5 这个节点来讲它的后序遍历(左 - 右 - 根)完成,打印 5,回退到上一层 8 节点。
8 节点左子树遍历完毕,访问其右子树 7 节点。
访问到右子树 7 节点,访问其左子树。
访问到左子树 4 节点,访问其左子树,发现左子树不存在,访问 4 的右子树,发现右子树也不存在。此时对于 4 这个节点来讲它的后序遍历(左 - 右 - 根)完成,打印 4,回退到上一层 7 节点。
7 节点左子树遍历完毕,访问其右子树 6 节点。
访问到右子树 6 节点,访问其左子树,发现左子树不存在,访问 6 的右子树,发现右子树也不存在。此时对于 6 这个节点来讲它的后序遍历(左 - 右 - 根)完成,打印 6,回退到上一层 7 节点。
7 节点右子树遍历完毕,此时对于 7 这个节点来讲它的后序遍历(左 - 右 - 根)完成,打印 7,回退到上一层 8 节点。
8 节点右子树遍历完毕,此时对于 8 这个节点来讲它的后序遍历(左 - 右 - 根)完成,打印 8,遍历结束。
后序遍历结果:9, 3, 5, 4, 6, 7, 8
2.3.1 代码实现
递归版本:
cpp
void Postorder(BTNode* phead)
{
if (phead == NULL)
{
return;
}
Postorder(phead->_left);
Postorder(phead->_right);
printf("%d ", phead->_data);
}
非递归版本(双栈法):
cpp
//非递归后序遍历
void PostorderNonR(BTNode* phead)
{
if (phead == NULL) {
printf("二叉树为空\n");
return;
}
ST stack1, stack2;
Init(&stack1);
Init(&stack2);
// 步骤1:根节点压入stack1
ST_push(&stack1, phead);
// 步骤2:处理stack1,将节点压入stack2
while (!STEmpty(&stack1)) {
BTNode* top_node = ST_top(&stack1);
ST_pop(&stack1);
// 步骤3:当前节点压入stack2
ST_push(&stack2, top_node);
// 步骤4:先压入左子节点,再压入右子节点(栈先进后出,保证右子节点先被处理)
if (top_node->_left != NULL) {
ST_push(&stack1, top_node->_left);
}
if (top_node->_right != NULL) {
ST_push(&stack1, top_node->_right);
}
}
// 步骤5:依次弹出stack2,即为后序遍历结果
while (!STEmpty(&stack2)) {
BTNode* top_node = ST_top(&stack2);
ST_pop(&stack2);
printf("%d ", top_node->_data);
}
// 销毁栈
Destory(&stack1);
Destory(&stack2);
}
代码逻辑可拆解为下面几个关键步骤,配合栈的操作如下:
检查根节点是否为空,若为空直接返回。初始化两个栈 stack1(工作栈)和 stack2(结果栈),并将根节点压入 stack1。
循环弹出 stack1 的栈顶节点:
- 将弹出的栈顶节点压入
stack2 - 将该栈顶结点的左孩子,右孩子分别压入
stack1。由于栈是后进先出,这保证了下一轮循环会优先处理右孩子,从而在stack2中形成根→右→左的顺序。 - 将
stack1栈顶节点弹出。
当 stack1 处理完毕后,stack2 中存储的就是逆序的后序遍历结果。依次弹出 stack2 的元素并打印,得到标准的左→右→根后序遍历序列。
遍历结束后销毁两个栈,避免内存泄漏。

三、广度优先遍历(层序遍历)
层序遍历是按二叉树的层级从上到下、同一层内从左到右依次访问所有节点的遍历方式,属于广度优先遍历(BFS),和前序 / 中序 / 后序的深度优先遍历(DFS) 遍历逻辑完全不同。
通俗点来讲,为了更好得利用和理解二叉树,我们将二叉树一层一层分开:

层序遍历要求我们从二叉树得第一层开始逐层开始往下遍历,比如上图的二叉树结构层序遍历的过程如下:
- 遍历第一层:8
- 遍历第二层:5,7
- 遍历第三层:9,3,4,6
综上,这个二叉树的层序遍历的结果就是8,5,7,9,3,4,6。
3.1 代码实现
代码实现层序遍历我们使用到了队列这个数据结构,首先我们将二叉树的根节点入队。然后我们不断将队列中的元素出队,同时将该元素的左右子节点分别入队(如果左右子节点不为空)直到队列中的元素全部出队为止。

cpp
void Levelorder(BTNode* phead)
{
if (phead == NULL)
{
printf("请传入有效的二叉树!\n");
return false;
}
Queue q1;
QueueInit(&q1);
QueuePush(&q1, phead);
while (!QueueEmpty(&q1))
{
QueueDataType front=QueueFrant(&q1);
printf("%d ",front->_data);
QueuePop(&q1);
if(front->_left!=NULL)
QueuePush(&q1, front->_left);
if (front->_right != NULL)
QueuePush(&q1, front->_right);
}
QueueDestory(&q1);
}

四、其它接口
4.1 计算节点总个数
如果要统计一个二叉树的节点的总的个数我们可以采用分治的思想:
节点总数量=左子树节点的数量+右子树节点的数量+1
cpp
int TreeSize(BTNode* phead)
{
if (phead == NULL)
{
return 0;
}
return TreeSize(phead->_left) + TreeSize(phead->_right) + 1;
}
4.2 计算叶子节点的个数
首先我们需要明确的是所谓叶子节点就是二叉树中没有左子节点和右子节点的节点(即_left == NULL 且 _right == NULL)
如果传入的节点指针为 NULL(表示空树或当前子树为空),则该子树的叶子节点数为 0。
如果当前节点的左、右子节点都为 NULL,说明当前节点是叶子节点,贡献 1 个叶子节点数。
如果当前节点不是叶子节点,则递归统计其左子树的叶子节点数 + 右子树的叶子节点数,结果相加即为当前子树的总叶子节点数。
cpp
int TreeLeafSize(BTNode* phead)
{
if (phead == NULL)
{
return 0;
}
if (phead->_left == NULL && phead->_right == NULL)
{
return 1;
}
else
{
return TreeLeafSize(phead->_left) + TreeLeafSize(phead->_right);
}
}
4.3 计算树的高度
二叉树的高度 :从根节点到最远叶子节点的最长路径上的节点数。所以二叉树的高度等于左子树高度和右子树高度中的较大值 + 1(+1 表示当前节点所在的层级)
例如:单节点树高度为 1;空树高度为 0。
cpp
int TreeHeight(BTNode* phead)
{
if (phead == NULL)
{
return 0;
}
int LTreeHeight = TreeHeight(phead->_left);
int RTreeHeight = TreeHeight(phead->_right);
return LTreeHeight > RTreeHeight ? LTreeHeight+1 : RTreeHeight+1;
}
首先,如果传入的节点指针为 NULL(空树或当前子树为空),则该子树的高度为 0。
然后分别递归计算当前节点的左子树高度和右子树高度。这样做的目的是将计算整棵树的高度拆解为计算左子树高度和计算右子树高度两个子问题,逐步向下递归,直到触达空树边界。
最后使用三元运算符取左、右子树高度的较大值,再加 1(表示当前节点为父节点的子树贡献了一层高度),作为当前子树的高度返回给上层递归。
4.5 计算第k层节点的个数
当我们要计算第k层的节点个数是对于当前节点的子节点来说,它们所在的层级是 k-1。
举个例子:
- 你站在根节点(第 1 层),想找整棵树的第 5 层节点。
- 对你的左孩子来说,它自己是第 2 层,所以它只需要找它自己的第 4 层(即 5-1)节点。
- 对你的左孩子的左孩子来说,它需要找它自己的第 3 层(即 4-1)节点。
- 以此类推,直到
k被减到 1,此时当前节点就是我们要找的目标节点。

cpp
int TreelayerSize(BTNode* phead,int k)
{
int height = TreeHeight(phead);
if (k > height||k<0)
{
printf("k参数非法!");
return -1;
}
if (k==0||phead == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
else
{
return TreelayerSize(phead->_left, k - 1) + TreelayerSize(phead->_right, k - 1);
}
}
4.6 判断二叉树是否是完全二叉树
什么是完全二叉树与非完全二叉树?
1. 完全二叉树(严格规则)
官方定义:一棵深度为 h 的二叉树,前 h−1 层是满二叉树(每层节点数达到最大值),第 h 层的节点从左到右连续排列,没有空隙。
把二叉树的节点按层序遍历顺序(从上到下、从左到右)给节点编号(从 1 开始),如果所有节点的编号都和满二叉的编号完全对应,没有中断,就是完全二叉树。

2. 非完全二叉树(打破规则)
只要不满足「完全二叉树」的条件,就是非完全二叉树。常见情况:
- 第 h 层的节点从左到右有空隙(左边缺节点,右边有节点)。
- 前 h−1 层不是满二叉树(中间某层缺节点)。

我们如何判断一个二叉树是不是完全二叉树呢?核心思路是层序遍历遇到空节点后,若再出现非空节点,则为非完全二叉树。
cpp
bool BinaryTreeComplete(BTNode* phead)
{
if (phead == NULL)
{
printf("请传入有效的二叉树!\n");
return false;
}
Queue q1;
QueueInit(&q1);
QueuePush(&q1, phead);
while (1)
{
QueueDataType front = QueueFrant(&q1);
QueuePop(&q1);
if (front == NULL)
{
break;
}
QueuePush(&q1, front->_left);
QueuePush(&q1, front->_right);
}
while (!QueueEmpty(&q1))
{
QueueDataType front = QueueFrant(&q1);
if (front != NULL)
{
return false;
}
QueuePop(&q1);
}
QueueDestory(&q1);
return true;
}
代码示例:


五、完整代码
cpp
#pragma once
#include<stdio.h>
#include<stdbool.h>
typedef int BTreeDatatype;
typedef struct BinaryTreeNode
{
BTreeDatatype _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
//前序遍历
void Preorder(BTNode* phead);
//非递归前序遍历
void PreorderNonR(BTNode* phead);
//中序遍历
void Inorder(BTNode* phead);
//非递归中序遍历
void InorderNonR(BTNode* phead);
//后序遍历
void Postorder(BTNode* phead);
//非递归后序遍历
void PostorderNonR(BTNode* phead);
//计算节点的总的个数
int TreeSize(BTNode* phead);
//计算叶子节点的个数
int TreeLeafSize(BTNode* phead);
//计算树的高度
int TreeHeight(BTNode* phead);
//计算第k层节点的个数
int TreelayerSize(BTNode* phead, int k);
// 创建一个新节点
BTNode* BuyNode(BTreeDatatype x);
//层序遍历二叉树
void Levelorder(BTNode* phead);
//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode * root);
cpp
#include"BinaryTree.h"
#include"Queue.h"
#include"Stack.h"
void Preorder(BTNode* phead)
{
if (phead == NULL)
{
return;
}
printf("%d ",phead->_data);
Preorder(phead->_left);
Preorder(phead->_right);
}
void PreorderNonR(BTNode* phead)
{
assert(phead);
ST st;
Init(&st);
//将根节点入栈
ST_push(&st, phead);
while (!STEmpty(&st))
{
BTNode* terget = ST_top(&st);//取栈顶的元素
ST_pop(&st);
printf("%d ",terget->_data);
if (terget->_right != NULL)
{
ST_push(&st, terget->_right);
}
if (terget->_left != NULL)
{
ST_push(&st, terget->_left);
}
}
Destory(&st);
}
void Inorder(BTNode* phead)
{
if (phead == NULL)
{
return;
}
Inorder(phead->_left);
printf("%d ", phead->_data);
Inorder(phead->_right);
}
//非递归中序遍历
void InorderNonR(BTNode* phead)
{
assert(phead);
ST st;
Init(&st);
//将根节点入栈
BTNode* cur = phead;
while (cur!=NULL||!STEmpty(&st))
{
//从该非空节点起遍历到最左侧沿途全入栈
while (cur)
{
ST_push(&st, cur);
cur = cur->_left;
}
BTNode* target = ST_top(&st);//取栈顶的元素
ST_pop(&st);
printf("%d ",target->_data);
cur = target->_right;
}
Destory(&st);
}
void Postorder(BTNode* phead)
{
if (phead == NULL)
{
return;
}
Postorder(phead->_left);
Postorder(phead->_right);
printf("%d ", phead->_data);
}
//非递归后序遍历
void PostorderNonR(BTNode* phead)
{
if (phead == NULL) {
printf("二叉树为空\n");
return;
}
ST stack1, stack2;
Init(&stack1);
Init(&stack2);
// 步骤1:根节点压入stack1
ST_push(&stack1, phead);
// 步骤2:处理stack1,将节点压入stack2
while (!STEmpty(&stack1)) {
BTNode* top_node = ST_top(&stack1);
ST_pop(&stack1);
// 步骤3:当前节点压入stack2
ST_push(&stack2, top_node);
// 步骤4:先压入左子节点,再压入右子节点(栈先进后出,保证右子节点先被处理)
if (top_node->_left != NULL) {
ST_push(&stack1, top_node->_left);
}
if (top_node->_right != NULL) {
ST_push(&stack1, top_node->_right);
}
}
// 步骤5:依次弹出stack2,即为后序遍历结果
while (!STEmpty(&stack2)) {
BTNode* top_node = ST_top(&stack2);
ST_pop(&stack2);
printf("%d ", top_node->_data);
}
// 销毁栈
Destory(&stack1);
Destory(&stack2);
}
int TreeSize(BTNode* phead)
{
if (phead == NULL)
{
return 0;
}
return TreeSize(phead->_left) + TreeSize(phead->_right) + 1;
}
int TreeLeafSize(BTNode* phead)
{
if (phead == NULL)
{
return 0;
}
if (phead->_left == NULL && phead->_right == NULL)
{
return 1;
}
else
{
return TreeLeafSize(phead->_left) + TreeLeafSize(phead->_right);
}
}
int TreeHeight(BTNode* phead)
{
if (phead == NULL)
{
return 0;
}
int LTreeHeight = TreeHeight(phead->_left);
int RTreeHeight = TreeHeight(phead->_right);
return LTreeHeight > RTreeHeight ? LTreeHeight+1 : RTreeHeight+1;
}
int TreelayerSize(BTNode* phead,int k)
{
int height = TreeHeight(phead);
if (k > height||k<0)
{
printf("k参数非法!");
return -1;
}
if (k==0||phead == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
else
{
return TreelayerSize(phead->_left, k - 1) + TreelayerSize(phead->_right, k - 1);
}
}
BTNode* TreeFind(BTNode* phead, int x)
{
if (phead == NULL)
{
return NULL;
}
if (phead->_data == x)
{
return phead;
}
BTNode* Ltarget= TreeFind(phead->_left, x);
if (Ltarget!= NULL)
{
return Ltarget;
}
BTNode* Rtarget = TreeFind(phead->_right, x);
if (Rtarget != NULL)
{
return Rtarget;
}
return NULL;
}
// 创建一个新节点
BTNode* BuyNode(BTreeDatatype x)
{
BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
if (newNode == NULL)
{
perror("malloc fail");
exit(1);
}
newNode->_data = x;
newNode->_left = NULL;
newNode->_right = NULL;
return newNode;
}
//层序遍历二叉树
void Levelorder(BTNode* phead)
{
if (phead == NULL)
{
printf("请传入有效的二叉树!\n");
return false;
}
Queue q1;
QueueInit(&q1);
QueuePush(&q1, phead);
while (!QueueEmpty(&q1))
{
QueueDataType front=QueueFrant(&q1);
QueuePop(&q1);
printf("%d ",front->_data);
if(front->_left!=NULL)
QueuePush(&q1, front->_left);
if (front->_right != NULL)
QueuePush(&q1, front->_right);
}
QueueDestory(&q1);
}
//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* phead)
{
if (phead == NULL)
{
printf("请传入有效的二叉树!\n");
return false;
}
Queue q1;
QueueInit(&q1);
QueuePush(&q1, phead);
while (1)
{
QueueDataType front = QueueFrant(&q1);
QueuePop(&q1);
if (front == NULL)
{
break;
}
QueuePush(&q1, front->_left);
QueuePush(&q1, front->_right);
}
while (!QueueEmpty(&q1))
{
QueueDataType front = QueueFrant(&q1);
if (front != NULL)
{
return false;
}
QueuePop(&q1);
}
QueueDestory(&q1);
return true;
}
cpp
#pragma once
#include<stdlib.h>
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include"BinaryTree.h"
typedef struct BinaryTreeNode* QueueDataType;
typedef struct QueueNode
{
struct QueueNode* _next;
QueueDataType _data;
}QNode;
typedef struct Queue
{
QNode* _phead;
QNode* _ptail;
int _size;
}Queue;
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
//队尾入队
void QueuePush(Queue* pq, QueueDataType x);
// 队头删除
void QueuePop(Queue* pq);
//获取队头与队尾的数据
QueueDataType QueueFrant(Queue* pq);
QueueDataType QueueBack(Queue* pq);
//队列判空
bool QueueEmpty(Queue* pq);
cpp
#include"Queue.h"
void QueueInit(Queue* pq)
{
assert(pq);
pq->_phead = NULL;
pq->_ptail = NULL;
pq->_size = 0;
}
//队列的销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->_phead;
while (cur)
{
QNode* next = cur->_next;
free(cur);
cur = next;
}
pq->_phead = pq->_ptail = NULL;
pq->_size = 0;
}
//队尾入队
void QueuePush(Queue* pq, QueueDataType x)
{
//创建新节点
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
printf("创建新节点失败!!");
return;
}
newnode->_data = x;
newnode->_next = NULL;
//队尾入队
if (pq->_ptail == pq->_phead && pq->_ptail == NULL)
{
//一个节点也没有
pq->_phead = newnode;
pq->_ptail = newnode;
}
else
{
//一个节点即以上
pq->_ptail->_next = newnode;
pq->_ptail = newnode;
}
pq->_size++;
}
// 队头删除
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->_size != 0);
QNode* next = pq->_phead;
if (pq->_phead->_next == NULL)
{
//只有一个节点
pq->_phead = pq->_ptail = NULL;
}
else
{
//一个节点以上
pq->_phead = pq->_phead->_next;
}
free(next);
pq->_size--;
}
//获取队头与队尾的数据
QueueDataType QueueFrant(Queue* pq)
{
assert(pq);
assert(pq->_size != 0);
return pq->_phead->_data;
}
QueueDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->_size != 0);
return pq->_ptail->_data;
}
//队列判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->_size == 0;
}
cpp
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#include"BinaryTree.h"
typedef BTNode* SLDatetype;
typedef struct Stack
{
SLDatetype* a;
size_t top;
size_t capacity;
}ST;
//初始化和销毁
void Init(ST* ch);
void Destory(ST* ch);
//入栈和出栈
void ST_push(ST* ch, SLDatetype x);
void ST_pop(ST* ch);
//取栈顶的数据
SLDatetype ST_top(ST* ch);
//判空
bool STEmpty(ST* ch);
//获取数据个数:
size_t ST_size(ST* ch);
cpp
#include"Stack.h"
void Init(ST* ch)
{
assert(ch);
ch->a = NULL;
ch->capacity = ch->top = 0;
}
void Destory(ST* ch)
{
assert(ch);
free(ch->a);
ch->a = NULL;
ch->capacity = ch->top = 0;
}
void ST_push(ST* ch, SLDatetype x)//入栈(压栈)
{
assert(ch);
//判断空间够不够:
if (ch->top == ch->capacity)
{
size_t newcapacity = ch->capacity == 0 ? 4 : 2 * ch->capacity;
SLDatetype* temp = (SLDatetype*)realloc(ch->a,newcapacity*sizeof(SLDatetype));
if (temp==NULL)
{
perror("realloc fail");
return;
}
ch->a = temp;
ch->capacity = newcapacity;
}
ch->a[ch->top] = x;
ch->top++;
}
void ST_pop(ST* ch)
{
assert(ch);
assert(ch->top>0);
ch->top--;
}
SLDatetype ST_top(ST* ch)//取栈顶的元素
{
assert(ch);
assert(ch->top>0);
return (ch->a[ch->top-1]);
}
bool STEmpty(ST* ch)
{
assert(ch);
return ch->top == 0;
}
size_t ST_size(ST* ch)
{
assert(ch);
return ch->top;
}
cpp
#include"Queue.h"
#include"Stack.h"
int main()
{
// 创建7个节点
BTNode* node1 = BuyNode(8);
BTNode* node2 = BuyNode(5);
BTNode* node3 = BuyNode(7);
BTNode* node4 = BuyNode(9);
BTNode* node5 = BuyNode(3);
BTNode* node6 = BuyNode(4);
BTNode* node7 = BuyNode(6);
// 构建完全二叉树结构
node1->_left = node2;
node1->_right = node3;
node2->_left = node4;
node2->_right = node5;
node3->_left = node6;
node3->_right = node7;
//计算节点的总的个数
printf("节点的总的个数为:%d\n",TreeSize(node1));
//计算叶子节点的个数
printf("叶子节点的总的个数为:%d\n", TreeLeafSize(node1));
//计算树的高度
printf("树的高度为:%d\n", TreeHeight(node1));
//计算第k层节点的个数
printf("第3层节点的个数为:%d\n", TreelayerSize(node1, 3));
//判断二叉树是否是完全二叉树
bool ret=BinaryTreeComplete(node1);
if (ret == true)
{
printf("是完全二叉树!");
}
else
{
printf("不是完全二叉树!");
}
printf("\n");
return 0;
}