前言
本文主要讲解二叉树的基础概念及其在 C 语言中的实现。
系统讲解了二叉树的数据结构基础,涵盖定义、术语、特殊类型及性质。重点阐述了二叉树的链式存储方式,并通过 C 语言代码实现了二叉树的构建、前中后序遍历、层序遍历、节点统计、高度计算、查找及完全二叉树判断功能,提供了完整的头文件与源文件代码示例。

本文主要讲解二叉树的基础概念及其在 C 语言中的实现。
树是一种非线性数据结构,它是由 n(n>=0)个有限结点组成一个具有层次关系的集合。 有一个特殊的结点,称为根结点,根结点没有前驱结点。 除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1、T2、……、Tm,其中每一个集合 Ti(1<= i <= m) 又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有 0 个或多个后继。
这是一个常见的树结构:

以下还有一些常见的术语:
二叉树就是一种特殊的树,其树的度最大为 2。也就是说,一个结点最多有两个分叉。而在日常生活中,由于树的结构复杂很少用,所以最实用的是二叉树,其只有最多两个分叉。
二叉树由一个根结点加上两棵别称为左子树和右子树的二叉树组成。且注意二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。

其组成就是:

二叉树有两种特殊情况:
如下图,分别是满二叉树和完全二叉树:

要注意,完全二叉树的编号是连续的,中间断开则不是完全二叉树。如下图的树就不是完全二叉树:

对于一个完全二叉树的存储,前面也提到完全二叉树的编号是连续的。所以,对于完全二叉树来说,我们可以用数组来进行存储,用下标来进行编号。二叉树顺序存储在物理上是一个数组,而在逻辑上是一颗二叉树。
如图,一个逻辑结构(我们人为想象的),一个物理结构(真实存储的):


最后一点,也是最重要的一点:
对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从 0 开始编号。存在:若父亲在数组中下标为 i,则该父亲的左孩子下标为 2 * i + 1;右孩子下标为 2 * i + 2。若孩子在数组中下标为 i,则该孩子的父亲下标为 (i - 1) / 2。注意:前提是这些下标所对应的值存在!!!**
前面我们说到存储完全二叉树可以用数组来进行,是因为完全二叉树的编号是连续的。而非完全二叉树却不是如此,若强行用数组来存储会浪费大量的空间。
那么除了数组,我们还可以用什么来储存二叉树呢?我们会想到链式存储。
链式存储:顾名思义,就是用链表来表示一颗二叉树,用链来指示元素的逻辑关系。通常的方法:创建一链表,使每个结点由三个域组成,数据域和左右指针域。左右指针分别用来给出该结点左孩子和右孩子所在结点的地址。
知道了用链式存储,现在就可以开始实现代码了。
与单链表的实现类似,二叉树中的每一个节点有以下三个成员:
本文以创建一个 char 类型的二叉树为例。
在写复杂程序时要养成写多个头文件&源文件的好习惯,这样条理就很清晰也不会乱。
BTNode.h 头文件,用于存放函数的声明和一些库函数的头文件。BTNode.c 源文件,用于放函数的定义 (二叉树的主体)。Test.c 源文件,用于测试实现的二叉树的运行效果。首先我们要定义一个二叉树。这里之前讲过,创建一个类似链表的结构,每个结点里面包含三个成员:节点中存储的数据、指向左孩子的指针、指向右孩子的指针。
代码演示:(内有注释,在头文件 BTNode.h 中写)
//重定义,方便修改类型
typedef char BTDataType; //表示每个节点的类型
typedef struct BinaryTreeNode {
BTDataType data; //节点中存储的数据
struct BinaryTreeNode* left; //指向左孩子的指针
struct BinaryTreeNode* right; //指向右孩子的指针
} BTNode;
在定义二叉树的代码中,有两个需要注意的点:
小编这里通过一个前序遍历的数组 "ABD##E#H##CF##G##" 来构建了二叉树。要先有一个二叉树,才能对二叉树进行操作。(关于怎么构建的不重要,大家也可以将一个一个结点进行插入,但这不是重点!!!重点是之后二叉树的实现!!!)
代码演示:
// 通过前序遍历的数组构建二叉树
BTNode* BinaryTreeCreate(BTDataType* ch, int* pi) {
if (ch[*pi] == '#') {
return NULL;
}
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
if (newnode == NULL) {
perror("malloc fail");
return NULL;
}
newnode->data = ch[(*pi)++];
newnode->left = BinaryTreeCreate(ch, pi);
(*pi)++;
newnode->right = BinaryTreeCreate(ch, pi);
return newnode;
}
要想对二叉树进行操作,肯定少不了遍历二叉树。但是要怎么遍历是个问题,这里一共有 4 种:前序遍历、中序遍历、后序遍历、层序遍历。这四种遍历主要在于遍历顺序的不一样,我们一一来拆解。
前序 前序遍历是指遍历时先遍历根、接着左子树、最后右子树。还要注意,这个遍历是递归进行的!!! 当遍历完根后,开始遍历左子树,就以左子树为起始位置,继续从根遍历、接着左子树、最后右子树,以此循环,直到全部遍历结束。
代码演示:
// 二叉树前序遍历
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);
}
这里看到小编特意把层序遍历与前三个遍历分开写,也可以看出层序的不一样了。也确实,层序遍历与前三种遍历差了很多。
首先,层序遍历是什么?层序层序,就是一层一层的遍历。比如下图的二叉树:该二叉树层序遍历的结果就是【1,2,3,4,5,6,7】。也就是一层一层遍历,这就是层序遍历。那又要该怎么写代码呢?
接着,代码思路是什么?我们怎么做到遍历完 1,2,3 后去遍历 4,5 呢?此时也无法直接通过 2 来找到 4,5 了。这时我们可以想到我们之前学过的东西——队列。我们可以在每次取出结点的同时,将该结点的左结点和右结点存储进队列中,直到队列为空。
例如: 开始时放入 1,此时队列【1】 取出 1,并放入 2、3,此时队列【2,3】 取出 2,并放入 4、5,此时队列【3,4,5】 取出 3,并放入 6、7,此时队列【4,5,6,7】 取出 4,此时队列【5,6,7】 取出 5,此时队列【6,7】 取出 6,此时队列【7】 取出 7,此时队列【】
在前面的学习中小编已经讲解过了队列了,这里就不赘述,想知道的可以去看看相关教程。
最后,代码实现。现在,知道了要用队列来实现,就可以开始来写代码了。我们创建一个队列,先把第一个根节点存入。接着在每次取出结点的同时,将该结点的左结点和右结点存储进队列中。以此循环直到队列为空。
代码演示:(不包含队列的实现)
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
//先创建一个队列来存储结点
Queue Q;
QueueInit(&Q);
//先将第一个根结点存入队列中
if (root) {
QueuePush(&Q, root);
}
//若队列不为空就循环
while (!QueueEmpty(&Q)) {
//接受队头的数据,并删除队头的结点
BTNode* root = QueueFront(&Q);
printf("%c ", root->data);
QueuePop(&Q);
//将队头的左子树、右子树存入队列中(前提是不为空)
if (root->left) {
QueuePush(&Q, root->left);
}
if (root->right) {
QueuePush(&Q, root->right);
}
}
//最后不要忘记了销毁队列
QueueDestroy(&Q);
}
当我们想要用代码来计数二叉树中的所有节点个数,该怎么办呢?其实这很简单,只需要熟悉递归就行了。当根节点为空时,就计 0。而当根节点为非空时,就 + 1 并递归计算其左右子树再。
代码演示:
// 二叉树节点个数
int BinaryTreeSize(BTNode* root) {
return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1; //递归左右子树并 +1
}
当我们想要用代码来计数二叉树的高度,该怎么办呢?首先我们要知道二叉树高度是什么,是树中结点的最大层次。而二叉树有左子树以及右子树,故左右子树高度更高的一边 + 1 就是树的高度。
所以,我们可以用递归来实现。当根节点为空时,返回 0。而当根节点为非空时,就返回左右子树高度更高的一边并 + 1(根节点本身)。
代码演示:
// 二叉树高度
int BinaryTreeHight(BTNode* root) {
//当根节点为空时,返回 0
if (root == NULL) {
return 0;
}
return fmax(BinaryTreeHight(root->left), BinaryTreeHight(root->right)) + 1; //而当根节点为非空时,返回左右子树高度更高的一边并 + 1(根节点本身)
}
当我们想要用代码来计数二叉树第 k 层的节点个数,该怎么办呢?对于一个满二叉树很简单,套公式就行,但非完全二叉树咋办呢?这里依旧是递归:初始 k,若该层不是目标层次,就 k - 1 并递归左右子树。当根节点为空时,返回 0。当根节点为非空且此时 k == 1 时,返回 1(已经到达第 k 层)。
代码演示:
// 二叉树第 k 层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) {
//当根节点为空时,返回 0
if (root == NULL) {
return 0;
}
//当根节点为非空且此时 k == 1 时,返回 1(已经到达第 k 层)
if (k == 1) {
return 1;
}
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1); //若该层不是目标层次,就 k - 1 并递归左右子树
}
当我们想要用代码来查找值为 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;
}
现在,给你一个树,怎么用代码来判断该树是完全二叉树呢?我们可以用到之前的层序遍历来完成。当层序遍历到空结点时,若之后还有数据则不是完全二叉树。当层序遍历到空结点时,若没有数据则是完全二叉树。
原理就是层序遍历,只不过多加入了一层判断。
代码演示:
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {
//先创建一个队列来存储结点
Queue Q;
QueueInit(&Q);
//先将第一个根结点存入队列中
if (root) {
QueuePush(&Q, root);
}
//若队列不为空就循环
while (!QueueEmpty(&Q)) {
//接受队头的数据,并删除队头的结点
BTNode* root = QueueFront(&Q);
QueuePop(&Q);
//判断是否遇到空结点,遇到就跳出循环
if (root == NULL) {
break;
}
//将队头的左子树、右子树存入队列中
QueuePush(&Q, root->left);
QueuePush(&Q, root->right);
}
//继续循环判断是否有残余结点
while (!QueueEmpty(&Q)) {
//接受队头的数据,并删除队头的结点
BTNode* root = QueueFront(&Q);
QueuePop(&Q);
//判断是否遇到非空结点,遇到则说明,结点不连续,不是完全二叉树
if (root != NULL) {
//最后不要忘记了销毁队列
QueueDestroy(&Q);
//返回 0(假)
return 0;
}
}
//最后不要忘记了销毁队列
QueueDestroy(&Q);
//最后返回 1(真)
return 1;
}
(包含队列的实现)
用于存放用来放函数的声明和一些库函数的头文件。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
//重定义,方便修改类型
typedef int QDataType; //表示每个节点的类型
typedef struct QListNode {
QDataType data; //节点中存储的数据
struct QListNode* next; //指向下一个节点的指针
} QNode;
// 队列的结构
typedef struct Queue {
QNode* front; //头指针
QNode* tail; //尾指针
int size; //总元素个数
} Queue;
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回 0
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
用于用来放函数的定义 (队列的主体)。
#include "Queue.h"
// 初始化队列
void QueueInit(Queue* q) {
assert(q); //断言空指针
q->front = NULL;
q->tail = NULL;
q->size = 0; //全部初始化置 0 / NULL
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data) {
assert(q); //断言空指针
QNode* tmp = (QNode*)malloc(sizeof(QNode)); //直接开辟一个节点的空间
if (tmp == NULL) //加一个 if 语句 防止增容失败
{
perror("malloc");
return;
}
//没有问题后就赋值
tmp->data = data;
tmp->next = NULL;
//注意:当队列中没有节点时,此时插入的节点既是头节点,又是尾节点
if (q->size == 0) {
q->front = tmp;
q->tail = tmp;
} else {
q->tail->next = tmp;
q->tail = tmp;
}
q->size++;
}
// 队头出队列
void QueuePop(Queue* q) {
assert(q);
assert(q->size > 0); //断言空指针,断言顺序表不能为空
//注意:当队列中只有一个节点时,头尾节点相等,此时将头节点和尾节点都释放
if (q->size == 1) {
free(q->front);
q->front = NULL;
q->tail = NULL;
} else {
QNode* del = q->front;
q->front = q->front->next;
free(del);
}
q->size--;
}
// 获取队列头部元素
QDataType QueueFront(Queue* q) {
assert(q);
assert(q->size > 0); //断言空指针,断言顺序表不能为空
return q->front->data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q) {
assert(q);
assert(q->size > 0); //断言空指针,断言顺序表不能为空
return q->tail->data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q) {
assert(q); //断言空指针
return q->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回 0
int QueueEmpty(Queue* q) {
assert(q); //断言空指针
return q->size == 0;
}
// 销毁队列
void QueueDestroy(Queue* q) {
assert(q); //断言空指针
QNode* cur = q->front; //遍历链表,一一释放空间销毁
while (cur) {
QNode* next = cur->next;
free(cur);
cur = next;
}
q->front = q->tail = NULL;
q->size = 0; //全部初始化置 0 / NULL
}
用于存放用来放函数的声明和一些库函数的头文件。
#pragma once
#include "Queue.h"
typedef char BTDataType;
typedef struct BinaryTreeNode {
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
} BTNode;
// 通过前序遍历的数组构建二叉树
BTNode* BinaryTreeCreate(BTDataType* ch, int* pi);
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树高度
int BinaryTreeHight(BTNode* root);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树第 k 层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为 x 的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
用于用来放函数的定义 (二叉树的主体)。
#include "BTNode.h"
// 通过前序遍历的数组构建二叉树
BTNode* BinaryTreeCreate(BTDataType* ch, int* pi) {
if (ch[*pi] == '#') {
return NULL;
}
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
if (newnode == NULL) {
perror("malloc fail");
return NULL;
}
newnode->data = ch[(*pi)++];
newnode->left = BinaryTreeCreate(ch, pi);
(*pi)++;
newnode->right = BinaryTreeCreate(ch, pi);
return newnode;
}
// 二叉树前序遍历
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);
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
//先创建一个队列来存储结点
Queue Q;
QueueInit(&Q);
//先将第一个根结点存入队列中
if (root) {
QueuePush(&Q, root);
}
//若队列不为空就循环
while (!QueueEmpty(&Q)) {
//接受队头的数据,并删除队头的结点
BTNode* root = QueueFront(&Q);
printf("%c ", root->data);
QueuePop(&Q);
//将队头的左子树、右子树存入队列中(前提是不为空)
if (root->left) {
QueuePush(&Q, root->left);
}
if (root->right) {
QueuePush(&Q, root->right);
}
}
//最后不要忘记了销毁队列
QueueDestroy(&Q);
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root) {
if (*root == NULL) {
return;
}
BinaryTreeDestory(&(*root)->left);
BinaryTreeDestory(&(*root)->right);
free(*root);
*root = NULL;
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root) {
return root == NULL ? 0 : 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);
}
// 二叉树高度
int BinaryTreeHight(BTNode* root) {
//当根节点为空时,返回 0
if (root == NULL) {
return 0;
}
return fmax(BinaryTreeHight(root->left), BinaryTreeHight(root->right)) + 1;
}
// 二叉树第 k 层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) {
//当根节点为空时,返回 0
if (root == NULL) {
return 0;
}
//当根节点为非空且此时 k == 1 时,返回 1(已经到达第 k 层)
if (k == 1) {
return 1;
}
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
// 二叉树查找值为 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;
}
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {
//先创建一个队列来存储结点
Queue Q;
QueueInit(&Q);
//先将第一个根结点存入队列中
if (root) {
QueuePush(&Q, root);
}
//若队列不为空就循环
while (!QueueEmpty(&Q)) {
//接受队头的数据,并删除队头的结点
BTNode* root = QueueFront(&Q);
QueuePop(&Q);
//判断是否遇到空结点,遇到就跳出循环
if (root == NULL) {
break;
}
//将队头的左子树、右子树存入队列中
QueuePush(&Q, root->left);
QueuePush(&Q, root->right);
}
//继续循环判断是否有残余结点
while (!QueueEmpty(&Q)) {
//接受队头的数据,并删除队头的结点
BTNode* root = QueueFront(&Q);
QueuePop(&Q);
//判断是否遇到非空结点,遇到则说明,结点不连续,不是完全二叉树
if (root != NULL) {
//最后不要忘记了销毁队列
QueueDestroy(&Q);
//返回 0(假)
return 0;
}
}
//最后不要忘记了销毁队列
QueueDestroy(&Q);
//最后返回 1(真)
return 1;
}
用于测试实现的二叉树的运行效果(这里是小编在写代码时写的测试用例,大家在写的时候也要多多测试哦)。
#include "BTNode.h"
int main() {
char ch[100] = "ABD##E#H##CF##G##";
int i = 0;
// 通过前序遍历的数组构建二叉树
BTNode* T = BinaryTreeCreate(ch, &i);
// 二叉树节点个数
int ret1 = BinaryTreeSize(T);
printf("二叉树节点个数:%d\n", ret1);
// 二叉树叶子节点个数
int ret2 = BinaryTreeLeafSize(T);
printf("二叉树叶子节点个数:%d\n", ret2);
// 二叉树高度
int ret3 = BinaryTreeHight(T);
printf("二叉树高度:%d\n", ret3);
// 二叉树第 k 层节点个数
int ret4 = BinaryTreeLevelKSize(T, 3);
printf("二叉树第 k 层节点个数:%d\n", ret4);
// 二叉树查找值为 x 的节点
BTNode* ret5 = BinaryTreeFind(T, 'G');
if (ret5 == NULL) {
printf("没有找到\n");
} else {
printf("存在%c\n", ret5->data);
}
// 判断二叉树是否是完全二叉树
int ret6 = BinaryTreeComplete(T);
if (ret6) {
printf("是完全二叉树\n");
} else {
printf("不是完全二叉树\n");
}
printf("\n");
// 二叉树前序遍历
printf("前序:");
BinaryTreePrevOrder(T);
printf("\n\n");
// 二叉树中序遍历
printf("中序:");
BinaryTreeInOrder(T);
printf("\n\n");
// 二叉树后序遍历
printf("后序:");
BinaryTreePostOrder(T);
printf("\n\n");
// 层序遍历
printf("层序:");
BinaryTreeLevelOrder(T);
printf("\n\n");
// 二叉树销毁
BinaryTreeDestory(&T);
return 0;
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online