一、二叉树
二叉树是每个节点最多有两个子节点的树形结构,是数据结构核心知识点,本文实现链式二叉树,包含创建、递归遍历、非递归层序遍历、节点统计、查找、销毁等核心接口
二、 二叉树结点结构定义
c
typedef int BTDataType;
// 二叉树节点结构体
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left; // 指向当前结点左孩子
struct BinaryTreeNode* right; // 指向当前结点右孩子
BTDataType data; // 当前结点值域
}BTNode;
BTNode 结构体通过 data 存储节点数据,并用 left 和right 两个指针分别指向节点的左孩子 和右孩子,以此串联形成二叉树的分支结构,是构建二叉树的基本单元
三、BinaryTree.h文件
用于二叉树接口声明
c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int BTDataType;
// 二叉树节点结构体
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left; // 指向当前结点左孩子
struct BinaryTreeNode* right; // 指向当前结点右孩子
BTDataType data; // 当前结点值域
}BTNode;
// 创建一个新节点
BTNode* BuyNode(BTDataType x);
// 前序遍历 (根 -> 左 -> 右)
void PreOrder(BTNode* root);
// 中序遍历 (左 -> 根 -> 右)
void InOrder(BTNode* root);
// 后序遍历 (左 -> 右 -> 根)
void PostOrder(BTNode* root);
// 求二叉树节点个数
int TreeSize(BTNode* root);
// 求二叉树叶子节点个数
int TreeLeafSize(BTNode* root);
// 求二叉树的高度
int TreeHeight(BTNode* root);
// 求第k层节点个数
int TreeLevelKSize(BTNode* root, int k);
// 查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x);
// 销毁二叉树
void TreeDestroy(BTNode** root);
// 层序遍历 (按层打印)
void LevelOrder(BTNode* root);
四、BinaryTree.c
函数接口实现
1. BuyNode 函数
BuyNode 函数实现逻辑:
该函数用于创建一个全新的二叉树节点,通过动态内存分配申请节点空间,校验内存是否申请成功,将传入的数据赋值给节点值域,并把左右孩子指针都初始化为空指针,最后返回新节点的地址,是构建二叉树的基础函数
函数功能:创建一个新节点
c
// 功能:创建一个新节点
// 参数:x为新节点存储的数据
// 返回值:创建好的新节点地址
BTNode* BuyNode(BTDataType x)
{
// 动态分配二叉树节点大小的内存空间
BTNode* NewNode = (BTNode*)malloc(sizeof(BTNode));
// 判断内存分配是否成功
if (NewNode == NULL)
{
// 打印内存分配失败的系统错误信息
perror("malloc failed");
// 分配失败直接退出程序
exit(-1);
}
// 将传入的数据赋值给新节点的数据域
NewNode->data = x;
// 初始化新节点左孩子指针为空
NewNode->left = NULL;
// 初始化新节点右孩子指针为空
NewNode->right = NULL;
// 返回创建完成的新节点
return NewNode;
}
2. PreOrder 函数
PreOrde 函数实现逻辑:
该函数采用递归方式实现二叉树前序遍历 ,遵循根→左→右 的访问顺序,先访问当前根节点,再递归遍历左子树,最后递归遍历右子树,遇到空节点打印 NULL并返回,是最基础的递归遍历方式
函数功能:前序遍历 (根 -> 左 -> 右)
c
// 功能:前序遍历 (根 -> 左 -> 右)
// 参数:root为二叉树根节点
void PreOrder(BTNode* root)
{
// 递归终止条件:当前节点为空
if (root == NULL)
{
// 打印空节点标识
printf("NULL ");
// 终止当前层递归
return;
}
// 先访问根节点,打印根节点数据
printf("%d ", root->data);
// 递归遍历左子树
PreOrder(root->left);
// 递归遍历右子树
PreOrder(root->right);
}
3. InOrder 函数
InOrder 函数实现逻辑:
该函数采用递归实现二叉树中序遍历 ,遵循左→根→右 的访问顺序,先递归遍历左子树,再访问根节点,最后递归遍历右子树,空节点打印 NULL 并返回,中序遍历二叉搜索树可得到有序序列
函数功能:中序遍历 (左 -> 根 -> 右)
c
// 功能:中序遍历 (左 -> 根 -> 右)
// 参数:root为二叉树根节点
void InOrder(BTNode* root)
{
// 递归终止条件:当前节点为空
if (root == NULL)
{
// 打印空节点标识
printf("NULL ");
// 终止当前层递归
return;
}
// 先递归遍历左子树
InOrder(root->left);
// 访问根节点,打印根节点数据
printf("%d ", root->data);
// 递归遍历右子树
InOrder(root->right);
}
4. PostOrder 函数
PostOrder 函数实现逻辑:
该函数采用递归实现二叉树后序遍历 ,遵循左→右→根 的访问顺序,先递归遍历左子树,再递归遍历右子树,最后访问根节点,空节点打印 NULL 并返回,节点销毁通常采用后序遍历逻辑
函数功能:后序遍历 (左 -> 右 -> 根)
c
// 功能:后序遍历 (左 -> 右 -> 根)
// 参数:root为二叉树根节点
void PostOrder(BTNode* root)
{
// 递归终止条件:当前节点为空
if (root == NULL)
{
// 打印空节点标识
printf("NULL ");
// 终止当前层递归
return;
}
// 先递归遍历左子树
PostOrder(root->left);
// 递归遍历右子树
PostOrder(root->right);
// 最后访问根节点,打印根节点数据
printf("%d ", root->data);
}
5. TreeSize 函数
TreeSize 函数实现逻辑:
该函数采用递归 分治思想计算二叉树总节点数,空节点返回 0,非空节点则统计当前 1 个节点 ,加上左子树节点总数和右子树节点总数,最终累加得到整棵树的节点个数
函数功能:求二叉树节点个数
c
// 功能:求二叉树节点个数
// 参数:root为二叉树根节点
// 返回值:二叉树总节点数
int TreeSize(BTNode* root)
{
// 递归终止条件:空树节点数为0
if (root == NULL)
{
return 0;
}
// 分治思想:总节点数 = 根节点(1) + 左子树节点数 + 右子树节点数
return 1 + TreeSize(root->left) + TreeSize(root->right);
}
6. TreeLeafSize 函数
TreeLeafSize 函数实现逻辑:
该函数递归统计二叉树叶子节点个数 ,叶子节点的定义是左右孩子都为空 ,满足条件则返回 1,否则递归累加左子树和右子树的叶子节点数量,最终得到总叶子节点数
函数功能:求二叉树叶子节点个数
c
// 功能:求二叉树叶子节点个数
// 参数:root为二叉树根节点
// 返回值:叶子节点总数量
int TreeLeafSize(BTNode* root)
{
// 递归终止条件:空节点叶子数为0
if (root == NULL)
{
return 0;
}
// 判断当前节点是否为叶子节点(左右孩子均为空)
if ((root->left == NULL) && (root->right == NULL))
{
// 是叶子节点,返回1
return 1;
}
// 非叶子节点,递归累加左、右子树的叶子节点数
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
7. TreeHeight 函数
TreeHeight **函数实现逻辑:
**该函数递归计算二叉树高度,空树高度为 0,非空树分别递归计算左子树高度和右子树高度 ,取两者较大值加 1(根节点所在层),最终得到整棵树的高度
函数功能:求二叉树的高度
c
// 功能:求二叉树的高度
// 参数:root为二叉树根节点
// 返回值:二叉树高度
int TreeHeight(BTNode* root)
{
// 递归终止条件:空树高度为0
if (root == NULL)
{
return 0;
}
// 递归计算左子树高度
int leftHeight = TreeHeight(root->left);
// 递归计算右子树高度
int rightHeight = TreeHeight(root->right);
// 树高度 = 1(根节点) + 左右子树高度的较大值
return 1 + (leftHeight >= rightHeight ? leftHeight : rightHeight);
}
8. TreeLevelKSize 函数
TreeLevelKSize **函数实现逻辑:**该函数递归求二叉树第 k 层节点个数,空节点返回 0,k=1 时表示到达目标层,返回 1,否则递归统计左子树 k-1 层和右子树 k-1 层的节点数,累加得到第 k 层总节点数
函数功能:求第 k层节点个数
c
// 功能:求第k层节点个数
// 参数:root为二叉树根节点,k为目标层数
// 返回值:第k层节点数量
int TreeLevelKSize(BTNode* root, int k)
{
// 递归终止条件1:空树节点数为0
if (root == NULL)
{
return 0;
}
// 递归终止条件2:k=1表示到达目标层,返回1
if (k == 1)
{
return 1;
}
// 递归统计:左子树k-1层 + 右子树k-1层 节点数
return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}
9. TreeFind 函数
TreeFind 函数实现逻辑:
该函数递归查找值为 x 的节点,先判断当前节点是否为空,为空返回 NULL,若当前节点数据等于 x 则返回当前节点,否则先递归查找左子树,找到则直接返回,左子树未找到再递归查找右子树,均未找到返回 NULL
函数功能:查找值为 x 的节点
c
// 功能:查找值为x的节点
// 参数:root为二叉树根节点,x为目标查找值
// 返回值:找到返回节点地址,未找到返回NULL
BTNode* TreeFind(BTNode* root, BTDataType x)
{
// 递归终止条件:空树,返回NULL
if (root == NULL)
{
return NULL;
}
// 判断当前节点数据是否等于目标值x
if (root->data == x)
{
// 找到目标节点,返回节点地址
return root;
}
// 递归查找左子树
BTNode* left = TreeFind(root->left, x);
// 左子树找到,直接返回节点地址
if (left != NULL)
{
return left;
}
// 递归查找右子树
BTNode* right = TreeFind(root->right, x);
// 右子树找到,直接返回节点地址
if (right != NULL)
{
return right;
}
// 左右子树均未找到,返回NULL
return NULL;
}
10. TreeDestroy 函数
TreeDestroy 函数实现逻辑:
该函数采用后序 递归方式销毁二叉树,先递归销毁左子树,再递归销毁右子树,最后释放当前节点内存并将指针置空,避免内存泄漏,使用二级指针保证根节点能被正确置空
函数功能:销毁二叉树
c
// 功能:销毁二叉树
// 参数:root为二叉树根节点的地址(二级指针)
void TreeDestroy(BTNode** root)
{
// 递归终止条件:节点为空,直接返回
if (*root == NULL)
{
return;
}
// 后序遍历销毁:先递归销毁左子树
TreeDestroy(&((*root)->left));
// 再递归销毁右子树
TreeDestroy(&((*root)->right));
// 最后释放当前节点的内存
free(*root);
// 将节点指针置空,防止野指针
*root = NULL;
}
11. LevelOrder 函数
LevelOrder 函数实现逻辑:
该函数采用队列实现二叉树层序遍历 ,按从上到下、从左到右 的顺序访问节点,先将根节点入队 ,循环取出队头节点打印,再将其非空左孩子、右孩子依次入队,直到队列为空,最后销毁队列,完成层序遍历
函数功能:层序遍历 (按层打印)
这个函数需要用到队列:队列博客
需要在Queue.h更改QueueDataType的类型
c
typedef int QueueDataType;
//改成:
typedef struct BinaryTreeNode* QueueDataType;
如果之前写过Queue队列这个数据结构,在vs可以这样添加文件
头文件->添加->现有项

找到代码存放的地方

找到Queue文件,复制Queue.c和Queue.h文件复制

粘贴在头文件

最后把Queue.c拉到源文件

要在BinaryTree.c文件加上Queue.h

c
// 功能:层序遍历 (按层打印)
// 参数:root为二叉树根节点
void LevelOrder(BTNode* root)
{
// 如果二叉树为空,打印提示信息并返回
if (root == NULL)
{
printf("BinaryTree is NULL\n");
return;
}
// 定义队列结构体变量
Queue Q;
// 初始化队列
QueueInit(&Q);
// 将根节点入队
QueuePush(&Q, root);
// 循环遍历,直到队列为空
while (!QueueEmpty(&Q))
{
// 获取队头节点数据
BTNode* top = QueueFront(&Q);
// 队头节点出队
QueuePop(&Q);
// 打印当前节点数据
printf("%d ", top->data);
// 如果左孩子非空,左孩子入队
if ((top->left) != NULL)
{
QueuePush(&Q, top->left);
}
// 如果右孩子非空,右孩子入队
if (top->right)
{
QueuePush(&Q, top->right);
}
}
// 遍历完成,销毁队列释放内存
QueueDestroy(&Q);
}
五、所有代码
BinaryTree.h
c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int BTDataType;
// 二叉树节点结构体
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left; // 指向当前结点左孩子
struct BinaryTreeNode* right; // 指向当前结点右孩子
BTDataType data; // 当前结点值域
}BTNode;
// 创建一个新节点
BTNode* BuyNode(BTDataType x);
// 前序遍历 (根 -> 左 -> 右)
void PreOrder(BTNode* root);
// 中序遍历 (左 -> 根 -> 右)
void InOrder(BTNode* root);
// 后序遍历 (左 -> 右 -> 根)
void PostOrder(BTNode* root);
// 求二叉树节点个数
int TreeSize(BTNode* root);
// 求二叉树叶子节点个数
int TreeLeafSize(BTNode* root);
// 求二叉树的高度
int TreeHeight(BTNode* root);
// 求第k层节点个数
int TreeLevelKSize(BTNode* root, int k);
// 查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x);
// 销毁二叉树
void TreeDestroy(BTNode** root);
// 层序遍历 (按层打印)
void LevelOrder(BTNode* root);
BinaryTree.c
c
#include "BinaryTree.h"
#include "Queue.h"
// 功能:创建一个新的二叉树节点,并初始化节点数据与左右孩子指针
// 参数:x - 节点存储的数据
// 返回:创建好的节点指针
BTNode* BuyNode(BTDataType x)
{
// 动态分配一个二叉树节点大小的内存
BTNode* NewNode = (BTNode*)malloc(sizeof(BTNode));
// 内存分配失败判断
if (NewNode == NULL)
{
perror("malloc failed"); // 打印错误信息
exit(-1); // 退出程序
}
// 给节点赋值
NewNode->data = x;
// 初始化左右孩子指针为空
NewNode->left = NewNode->right = NULL;
// 返回新节点
return NewNode;
}
// 功能:二叉树前序遍历(根 -> 左子树 -> 右子树)
// 参数:root - 二叉树根节点
void PreOrder(BTNode* root)
{
// 递归终止条件:节点为空,打印 NULL 并返回
if (root == NULL)
{
printf("NULL ");
return;
}
// 先访问根节点
printf("%d ", root->data);
// 递归遍历左子树
PreOrder(root->left);
// 递归遍历右子树
PreOrder(root->right);
}
// 功能:二叉树中序遍历(左子树 -> 根 -> 右子树)
// 参数:root - 二叉树根节点
void InOrder(BTNode* root)
{
// 递归终止条件:节点为空,打印 NULL 并返回
if (root == NULL)
{
printf("NULL ");
return;
}
// 递归遍历左子树
InOrder(root->left);
// 访问根节点
printf("%d ", root->data);
// 递归遍历右子树
InOrder(root->right);
}
// 功能:二叉树后序遍历(左子树 -> 右子树 -> 根)
// 参数:root - 二叉树根节点
void PostOrder(BTNode* root)
{
// 递归终止条件:节点为空,打印 NULL 并返回
if (root == NULL)
{
printf("NULL ");
return;
}
// 递归遍历左子树
PostOrder(root->left);
// 递归遍历右子树
PostOrder(root->right);
// 最后访问根节点
printf("%d ", root->data);
}
// 功能:求二叉树的总节点个数
// 思路:树的节点数 = 1(根) + 左子树节点数 + 右子树节点数
// 参数:root - 二叉树根节点
// 返回:总节点个数
int TreeSize(BTNode* root)
{
// 空树节点数为 0
if (root == NULL)
{
return 0;
}
// 递归计算
return 1 + TreeSize(root->left) + TreeSize(root->right);
}
// 功能:求二叉树的叶子节点个数(叶子:左右孩子都为空的节点)
// 参数:root - 二叉树根节点
// 返回:叶子节点个数
int TreeLeafSize(BTNode* root)
{
// 空树叶子节点数为 0
if (root == NULL)
{
return 0;
}
// 当前节点是叶子节点,返回 1
if ((root->left == NULL) && (root->right == NULL))
{
return 1;
}
// 递归统计左子树 + 右子树的叶子节点
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
// 功能:求二叉树的高度(深度)
// 思路:树高 = 1 + 左右子树高度的较大值
// 参数:root - 二叉树根节点
// 返回:树的高度
int TreeHeight(BTNode* root)
{
// 空树高度为 0
if (root == NULL)
{
return 0;
}
// 递归求左子树高度
int leftHeight = TreeHeight(root->left);
// 递归求右子树高度
int rightHeight = TreeHeight(root->right);
// 当前树高度 = 1 + 较高子树的高度
return 1 + (leftHeight >= rightHeight ? leftHeight : rightHeight);
}
// 功能:求二叉树第 k 层的节点个数
// 思路:第k层节点数 = 左子树k-1层节点数 + 右子树k-1层节点数
// 参数:root - 二叉树根节点;k - 层数(从1开始)
// 返回:第k层节点个数
int TreeLevelKSize(BTNode* root, int k)
{
// 空树,任意层节点数都为0
if (root == NULL)
{
return 0;
}
// k=1 表示当前层,只有根节点1个
if (k == 1)
{
return 1;
}
// 递归到下一层
return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}
// 功能:在二叉树中查找值为 x 的节点
// 思路:先查根,再查左子树,左子树找不到再查右子树
// 参数:root - 二叉树根节点;x - 要查找的值
// 返回:找到返回节点指针,没找到返回 NULL
BTNode* TreeFind(BTNode* root, BTDataType x)
{
// 空树,直接返回 NULL
if (root == NULL)
{
return NULL;
}
// 根节点就是目标节点,直接返回
if (root->data == x)
{
return root;
}
// 递归查找左子树
BTNode* left = TreeFind(root->left, x);
// 左子树找到,直接返回结果
if (left != NULL)
{
return left;
}
// 递归查找右子树
BTNode* right = TreeFind(root->right, x);
// 右子树找到,直接返回结果
if (right != NULL)
{
return right;
}
// 左右都没找到,返回 NULL
return NULL;
}
// 功能:销毁二叉树,释放所有节点内存
// 思路:后序释放(先释放孩子,再释放根,避免内存泄漏)
// 参数:二级指针 root - 可以修改实参,将根置空
void TreeDestroy(BTNode** root)
{
// 根节点为空,无需释放
if (*root == NULL)
{
return;
}
// 递归销毁左子树
TreeDestroy(&((*root)->left));
// 递归销毁右子树
TreeDestroy(&((*root)->right));
// 释放当前节点
free(*root);
// 将根指针置空,防止野指针
*root = NULL;
}
// 功能:二叉树层序遍历(从上到下、从左到右逐层访问)
// 实现:借助队列,先入队根节点,然后出队时将左右孩子入队
// 参数:root - 二叉树根节点
void LevelOrder(BTNode* root)
{
// 空树直接提示
if (root == NULL)
{
printf("BinaryTree is NULL\n");
return;
}
// 定义队列并初始化
Queue Q;
QueueInit(&Q);
// 根节点先入队
QueuePush(&Q, root);
// 队列不为空,持续遍历
while (!QueueEmpty(&Q))
{
// 取队头节点
BTNode* top = QueueFront(&Q);
// 出队
QueuePop(&Q);
// 访问当前节点数据
printf("%d ", top->data);
// 左孩子不为空,入队
if ((top->left) != NULL)
{
QueuePush(&Q, top->left);
}
// 右孩子不为空,入队
if (top->right)
{
QueuePush(&Q, top->right);
}
}
// 遍历完成,销毁队列
QueueDestroy(&Q);
}
test.c
c
#include "BinaryTree.h"
// 功能:手动构建一棵二叉树(用于测试)
// 构建的树结构:
// 1
// / \
// 2 3
// / \ / \
// 4 5 6 7
// /
// 8
void test_BuyNode(BTNode** root)
{
// 创建根节点 1
*root = BuyNode(1);
// 创建所有子节点
BTNode* Node1 = BuyNode(2); // 根左孩子
BTNode* Node2 = BuyNode(3); // 根右孩子
BTNode* Node3 = BuyNode(4); // 2 的左孩子
BTNode* Node4 = BuyNode(5); // 2 的右孩子
BTNode* Node5 = BuyNode(6); // 3 的左孩子
BTNode* Node6 = BuyNode(7); // 3 的右孩子
BTNode* Node7 = BuyNode(8); // 7 的左孩子
// 建立节点之间的父子关系
(*root)->left = Node1; // 根 1 左孩子 = 2
(*root)->right = Node2; // 根 1 右孩子 = 3
Node1->left = Node3; // 2 左孩子 = 4
Node1->right = Node4; // 2 右孩子 = 5
Node2->left = Node5; // 3 左孩子 = 6
Node2->right = Node6; // 3 右孩子 = 7
Node6->left = Node7; // 7 左孩子 = 8
}
// 功能:测试前序遍历
void test_PreOrder(BTNode* root)
{
printf("前序遍历:");
PreOrder(root); // 调用前序遍历函数
printf("\n");
}
// 功能:测试中序遍历
void test_InOrder(BTNode* root)
{
printf("中序遍历:");
InOrder(root); // 调用中序遍历函数
printf("\n");
}
// 功能:测试后序遍历
void test_PostOrder(BTNode* root)
{
printf("后序遍历:");
PostOrder(root); // 调用后序遍历函数
printf("\n");
}
// 功能:测试求二叉树总节点数
void test_TreeSize(BTNode* root)
{
printf("二叉树总节点数:%d \n", TreeSize(root));
}
// 功能:测试求二叉树叶子节点数
void test_TreeLeafSize(BTNode* root)
{
printf("二叉树叶子节点数:%d \n", TreeLeafSize(root));
}
// 功能:测试求二叉树高度(深度)
void test_TreeHeight(BTNode* root)
{
printf("二叉树高度:%d \n", TreeHeight(root));
}
// 功能:测试求二叉树第 K 层节点数(这里测试第4层)
void test_TreeLevelKSize(BTNode* root)
{
printf("二叉树第4层节点数:%d \n", TreeLevelKSize(root, 4));
}
// 功能:测试查找值为 x 的节点(这里查找 0,不存在)
void test_TreeFind(BTNode* root)
{
if (TreeFind(root, 0)) // 查找值为 0 的节点
{
printf("查找结果:找到节点 0\n");
}
else
{
printf("查找结果:未找到节点 0\n");
}
}
// 功能:测试层序遍历(从上到下逐层打印)
void test_LevelOrder(BTNode* root)
{
printf("层序遍历:");
LevelOrder(root);
printf("\n");
}
int main()
{
BTNode* R; // 定义二叉树根节点指针
// 1. 构建二叉树
test_BuyNode(&R);
// 2. 打开注释即可测试对应功能
//test_PreOrder(R); // 前序遍历测试
//test_InOrder(R); // 中序遍历测试
//test_PostOrder(R); // 后序遍历测试
//test_TreeSize(R); // 总节点数测试
//test_TreeLeafSize(R); // 叶子节点数测试
//test_TreeHeight(R); // 树高度测试
//test_TreeLevelKSize(R); // 第k层节点数测试
//test_TreeFind(R); // 查找节点测试
//test_LevelOrder(R); // 层序遍历测试
// 3. 销毁二叉树,释放内存(必须调用,防止内存泄漏)
TreeDestroy(&R);
return 0;
}