⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:数据结构初阶
⭐代码仓库:Data Structure
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!
二叉树的链式结构
前言
创建一个二叉树的基本结构难度较大,我们看标题知道这个二叉树为链式的结构,链式结构的基本概念我在之前的博客中已经写过,相信大家肯定有所了解了,但是我们如何创建一个二叉树成为一个比较大的问题,二叉树是一个树的形状,数据存储是可以理解成断层的,所以增删改查似乎也没有什么很大的事迹意义了,所以我们这里会手动简单链接一个二叉树,让大家更好地理解。
一、三板斧
理解了那么多概念,三板斧是不可以忘掉的,我们创建两个源文件和一个头文件,如下:
二、二叉树的基本结构
我们先来一张完全二叉树的图:
大家从图中可以看出1作为根节点,其左节点为2,右节点为4。所以二叉树的链式结构为左节点和右节点,我们就可以定义一下二叉树放到struct结构体里面中:
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
三、创建新节点
创建新节点可以说是我们的老朋友了,用malloc动态申请一个新结点,将左右结点指向空(避免野指针),数据填进去即可:
//创立一个新节点
BTNode* BuyNode(BTDataType x)
{
BTNode* BTnode = (BTNode*)malloc(sizeof(BTNode));
if (BTnode == NULL)
{
perror("malloc fail");
return;
}
BTnode->data = x;
BTnode->left = NULL;
BTnode->right = NULL;
return BTnode;
}
四、手搓一个简单的二叉树
这里简单写一个二叉树的代码,是直接指针进行链接的,方便一点让大家好理解,因为这个创立一个真正的二叉树是需要用到前序遍历和中序遍历还有后序遍历的,所以我们简单地实现一个二叉树:
//创立一个二叉树
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
BTNode* node7 = BuyNode(7);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node2->right = node7;
node4->left = node5;
node4->right = node6;
//node3->right = node7;
return node1;
}
五、二叉树遍历(递归)
二叉树遍历是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
- 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前,即:根、左子树、右子树。
- 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间),即:左子树、根、右子树。
- 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后,即:左子树、根、右子树。
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
1、前序遍历
(1)思路
这里的前序、中序和后序遍历看起来很难,但是代码只有几行,可是思路难度有点大,在刚进行遍历的时候我们要想一想递归是什么,可以理解为在栈区开辟很多个栈帧,我们利用寄存器去进行访问不同的栈帧,这就是递归的本质。我们拿上面的完全二叉树的图进行举例画个图:
先找根再找左子树再找右子树,我们模拟一下:先找到根为1,输出1,我们找左子树2,发现2为根,输出2,那就找以2为根的左子树,找到了3,发现3是叶子结点,输出3,那再找左子树NULL,找到末尾了,再找右子树为NULL,那就以,2为根的左子树找完了,找右子树7,发现7为叶子结点,找左子树为NULL,右子树为NULL,继续往上递归,1为根的子树左边已经找完了,找1为根的右子树,先找到4,为根,以4为根的左子树为5,5为叶子结点,左子树为NULL,右子树为NULL,以4为根的左子树已经找完了,找以4为根的右子树为6,6为叶子结点,找左子树NULL,右子树NULL即可。
(2)代码
代码如下:
//前序 -- 根、左子树、右子树
void PreOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
(3)递归过程
2、中序遍历
(1)思路
再有了前序遍历的基础,做中序遍历那就更加简单了,我们的思路为左子树、根、右子树遍历。我们继续拿我们上面画的图进行讲解:从根结点1出发,找左子树2,2又为根节点,继续找左子树3, 3为叶子结点,找左子树NULL,返回找根为3,输出3,找右子树NULL,再返回,2的左子树找完,回到2的位置,输出2,找2的右子树为7,7又是叶子结点,找7的左子树为NULL,返回找根为7,输出7,找7的右子树为NULL,并返回,以1为根的左子树已经全部找完,返回根节点1,输出1,再找1的右子树为4,4又是根,找4的左子树为5,5为叶子结点,找5的左子树为NULL,返回找根为5,输出5,找5的右子树为NULL,然后回去找根为4,输出4,再找以4为根的右子树为6,6为叶子结点,再找6的左子树为NULL,返回,再找根节点为6,输出6,再找6的右子树为NULL。
(2)代码
//中序 -- 左子树、根、右子树
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
(3)递归过程
3、后序遍历
(1)思路
我们继续拿上面的图片来进行讲解:后序遍历的过程是左子树、右子树、根。从1开始往左子树找,2为根,继续往左子树找,3为叶子结点,继续往左子树找,找到NULL,再以3为根找右子树NULL,再找根为3,输出3,以2为根的左子树找完了,再找右子树发现7为叶子结点,继续找左子树为NULL,右子树为NULL,再找根为7,输出7,以2为根结点的左右子树都已经找好了,输出2,以1为根的左子树也都找好了,找以1为根节点的右子树,4为根,继续往左找左子树,5为根结点,继续找左子树为NULL,右子树也为NULL,再找到以4为根的右子树,根为6,继续找左子树NULL,右子树NULL,找完左右子树找根为6,输出6,以4为根的左右子树都已经找好了,找根为4,输出4,以1为根的左右子树都已经找好了,找根为1,输出1。
(2)代码
//后序 -- 左子树、右子树、根
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
(3)递归过程
六、算结点个数
1、思路
总体的思路是指挥打工人将数目报上来,我们举个简单的例子,学院要统计人数,院长指挥导员报人数,导员指挥班长报人数,班长指挥舍长报人数,我们画个图很好理解:
2、代码
//算结点个数 -- 指挥打工人统计数量报上来
int TreeSize(BTNode* root)
{
//指挥打工人
return root == NULL ? 0 :
TreeSize(root->left)
+ TreeSize(root->right)
+ 1;
}
3、递归过程
七、算当前树的高度
1、思路
思路还是使用递归,记录左子树的层数以及右子树的层数,哪个大就用哪个层数再加1。
2、代码
//当前树的高度 == 左右子树高的那个层数+1
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int LeftTreeHeight = TreeHeight(root->left);
int RightTreeHeight = TreeHeight(root->right);
return LeftTreeHeight > RightTreeHeight ?
LeftTreeHeight + 1
: RightTreeHeight + 1;
}
3、递归过程
八、二叉树第k层节点
1、思路
思路较为清奇,也就是算当前树的第k层结点个数也就是算左子树第k-1层个数加上右子树第k-1层结点个数。但这里有个细节,就是当k等于1的时候返回的是1,因为k为1的时候是必定有一个结点的,而如果不加这个条件,返回的是0。
2、代码
//二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
//当前树的第k层的个数 = 左子树的第k-1层个数 + 右子树的第k-1层个数
if (root == NULL)
return 0;
if (k == 1)
return 1;
int LeftK = BinaryTreeLevelKSize(root->left, k - 1);
int RightK = BinaryTreeLevelKSize(root->right, k - 1);
return LeftK + RightK;
}
3、递归过程
九、二叉树查找值为x的节点
1、思路
思路很简单的,只需要判断值是否相等即可,再进行递归找值即可。
2、代码
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* lret = BinaryTreeFind(root->left, x);
if (lret)
return lret;
BTNode* rret = BinaryTreeFind(root->right, x);
if (rret)
return rret;
}
3、递归过程
十、层序遍历
1、思路
这次的层序遍历是利用一个队列进行解决,我们之前写过一个队列,这里我们就不详细的把队列代码写出来了,就简单说一下这几个函数代表什么:QueueInit(&q);初始化队列,结点前后都指向空,size等于0。QueuePush(&q, root);进队列,也就是链表的尾插,将新结点连接进去。QueueEmpty(&q);判断队列是否为空,头和尾是否同时为空。QueueFront(&q);是取出队头的数据。QueuePop(&q);是将链表头删。QueueDestroy(&q);是将链表销毁掉。也就是说,是先将树的根结点先放进队列,记录队头数据,pop出队头数据,再放入下一层以pop出队列的那个根节点的左右孩子的数据进队列即可,直到出完整个队列的结点。
2、代码
//层序遍历
void LevelOrder(BTNode* root)
{
//先创建一个队列
Queue q;
QueueInit(&q);
//根结点不为空则插入到队列当中
if (root)
QueuePush(&q, root);
//往后遍历
//出一个头结点进下一层结点
while (!QueueEmpty(&q))
{
//拿出队头结点
BTNode* front = QueueFront(&q);
//把队列中的pop掉这样不会导致树里面的数据丢失
QueuePop(&q);
printf("%d ", front->data);
//push左树和右树
if (front->left)
QueuePush(&q, front->left);
if (front->right)
QueuePush(&q, front->right);
}
printf("\n");
QueueDestroy(&q);
}
3、演示
十一、二叉树销毁
1、思路
遍历free左子树和右子树即可。
2、代码
// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
if (root == NULL)
return;
BinaryTreeDestory(root->left);
BinaryTreeDestory(root->right);
free(root);
}
十二、二叉树叶子节点个数
1、思路
root是空则返回0,当left和right都为空,则只返回这一个结点的个数为1,而当碰到一个根节点下面只有一个结点的则返回1+1。是一个递归的过程。
2、代码
// 二叉树叶子节点个数
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);
}
3、递归过程
十三、判断二叉树是否是完全二叉树
1、思路
我们知道二叉树可以说是连续的,我们看上面两张图,左图为普通二叉树,而右图为完全二叉树,也就是我们的结点是连续的,我们需要利用层序遍历的思路,利用队列的思想,因为层序遍历是一层一层从左往右出结点,当我们进行层序遍历遇到空的时候,也就是我们左图2为根节点的右孩子为空的时候,我们跳出这个队列,然后我们再继续利用层序遍历的思想,如果后面进行层序遍历发现全是NULL指针,那就是完全二叉树,如果后面还有结点数据,那就不是完全二叉树,只是一个普通二叉树,详情看我的演示环节,我会利用上面两棵树进行队列的演示。
2、代码
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
//根结点不为空则插入到队列当中
if (root)
QueuePush(&q, root);
//往后走,到NULL则退出,取完数据
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* front1 = QueueFront(&q);
QueuePop(&q);
//判断队头数据是否为空,为空则继续走,不为空则输出false
if (front1)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
3、演示
十四、创建二叉树
这个是加餐了,前面我们只是简单地手搓了一个二叉树,而这里我们在牛客网上实现一个二叉树,是要利用前序和中序遍历进行实现的,下面为链接,再下面是代码和实现过程:
#include <stdio.h>
#include<stdlib.h>
typedef struct TreeNode {
struct TreeNode* left;
struct TreeNode* right;
char val;
} TreeNode;
//创建一个二叉树
TreeNode* CreatTree(char* a, int* pi)
{
if(a[*pi] == '#')
{
(*pi)++;
return NULL;
}
TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
root->val = a[(*pi)];
(*pi)++;
root->left = CreatTree(a, pi);
root->right = CreatTree(a, pi);
return root;
}
//中序遍历
void InOrder(TreeNode* root)
{
if(root == NULL)
return;
InOrder(root->left);
printf("%c ",root->val);
InOrder(root->right);
}
int main() {
char a[100];
scanf("%s", a);
int i = 0;
TreeNode* root = CreatTree(a, &i);
InOrder(root);
return 0;
}
十五、测试
我们利用下面的代码进行测试:
int main()
{
BTNode* root = CreatBinaryTree();
PreOrder(root);
printf("\n");
InOrder(root);
printf("\n");
PostOrder(root);
printf("\n");
int size = TreeSize(root);
printf("TreeSize:%d\n", size);
int size1 = TreeSize(root);
printf("TreeSize:%d\n", size1);
int height = TreeHeight(root);
printf("HeightSize:%d\n", height);
int K1 = BinaryTreeLevelKSize(root, 3);//层数
printf("BinaryTreeLevelKSize:%d\n", K1);
int K2 = BinaryTreeLevelKSize(root, 2);//层数
printf("BinaryTreeLevelKSize:%d\n", K2);
printf("地址为:%p\n", BinaryTreeFind(root, 4));
printf("地址为:%p\n", BinaryTreeFind(root, 50));
printf("LevelOrder:");
LevelOrder(root);
printf("判断是否为完全二叉树:");
printf("%d\n", BinaryTreeComplete(root));
printf("该二叉树的叶子结点为:");
printf("%d", BinaryTreeLeafSize(root));
BinaryTreeDestory(root);
return 0;
}
测试结果:
十六、源代码
BinaryTreeNode.h:
//用链表实现
//先进先出
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<stdbool.h>
typedef struct BinaryTreeNode* QueueDataType;
typedef struct QueueNode {
struct QueueNode* next;
QueueDataType data; //结构体指针,指向的是树里面的结点
}QNode;
typedef struct Queue {
QNode* head;
QNode* tail;
int size;
}Queue;
//初始化
void QueueInit(Queue* pq); //改变结构体成员就用结构体的指针即可
//销毁链表
void QueueDestroy(Queue* pq);
//进队列
void QueuePush(Queue* pq, QueueDataType x);
//出队列
void QueuePop(Queue* pq);
//计算长度
int QueueSize(Queue* pq);
//判断是否为空
bool QueueEmpty(Queue* pq);
//队头
QueueDataType QueueFront(Queue* pq);
//队尾
QueueDataType QueueBack(Queue* pq);
BinaryTreeNode.c:
#include"BinaryTreeNode.h"
//初始化
void QueueInit(Queue* pq) {
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
//销毁链表
void QueueDestroy(Queue* pq) {
assert(pq);
QNode* cur = pq->head;
while (cur) {
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
//进队列
void QueuePush(Queue* pq, QueueDataType x) {
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL) {
perror("malloc failed");
return;
}
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL) {//链表内部啥也没有
assert(pq->tail == NULL);
pq->head = pq->tail = newnode;
}
else {
pq->tail->next = newnode;
pq->tail = newnode;
}
pq->size++;
}
//出队列
void QueuePop(Queue* pq) {
assert(pq);
assert(pq->head != NULL);
/*QNode* cur = pq->head;
QNode* next = cur->next;
free(cur);
pq->head = next;
if (pq->head == NULL) {
pq->tail = NULL;
}
pq->size--;*/
if (pq->head->next == NULL) {
free(pq->head);
pq->tail = pq->head = NULL;
}
else {
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
pq->size--;
}
//计算长度
int QueueSize(Queue* pq) {
assert(pq);
return pq->size;
}
//判断是否为空
bool QueueEmpty(Queue* pq) {
assert(pq);
return pq->head == NULL && pq->tail == NULL;
}
//取队头
QueueDataType QueueFront(Queue* pq) {
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
//队尾
QueueDataType QueueBack(Queue* pq) {
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
Test.c:
//递归
#include"BinaryTreeNode.h"
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
//创立一个新节点
BTNode* BuyNode(BTDataType x)
{
BTNode* BTnode = (BTNode*)malloc(sizeof(BTNode));
if (BTnode == NULL)
{
perror("malloc fail");
return;
}
BTnode->data = x;
BTnode->left = NULL;
BTnode->right = NULL;
return BTnode;
}
//创立一个二叉树
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
BTNode* node7 = BuyNode(7);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node2->right = node7;
node4->left = node5;
node4->right = node6;
//node3->right = node7;
return node1;
}
//前序 -- 根、左子树、右子树
void PreOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
//中序 -- 左子树、根、右子树
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
//后序 -- 左子树、右子树、根
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
//算结点个数 -- 指挥打工人统计数量报上来
int TreeSize(BTNode* root)
{
//指挥打工人
return root == NULL ? 0 :
TreeSize(root->left)
+ TreeSize(root->right)
+ 1;
}
//当前树的高度 == 左右子树高的那个层数+1
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int LeftTreeHeight = TreeHeight(root->left);
int RightTreeHeight = TreeHeight(root->right);
return LeftTreeHeight > RightTreeHeight ?
LeftTreeHeight + 1
: RightTreeHeight + 1;
}
//二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
//当前树的第k层的个数 = 左子树的第k-1层个数 + 右子树的第k-1层个数
if (root == NULL)
return 0;
if (k == 1)
return 1;
int LeftK = BinaryTreeLevelKSize(root->left, k - 1);
int RightK = BinaryTreeLevelKSize(root->right, k - 1);
return LeftK + RightK;
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* lret = BinaryTreeFind(root->left, x);
if (lret)
return lret;
BTNode* rret = BinaryTreeFind(root->right, x);
if (rret)
return rret;
}
//层序遍历
void LevelOrder(BTNode* root)
{
//先创建一个队列
Queue q;
QueueInit(&q);
//根结点不为空则插入到队列当中
if (root)
QueuePush(&q, root);
//往后遍历
//出一个头结点进下一层结点
while (!QueueEmpty(&q))
{
//拿出队头结点
BTNode* front = QueueFront(&q);
//把队列中的pop掉这样不会导致树里面的数据丢失
QueuePop(&q);
printf("%d ", front->data);
//push左树和右树
if (front->left)
QueuePush(&q, front->left);
if (front->right)
QueuePush(&q, front->right);
}
printf("\n");
QueueDestroy(&q);
}
// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
if (root == NULL)
return;
BinaryTreeDestory(root->left);
BinaryTreeDestory(root->right);
free(root);
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
//根结点不为空则插入到队列当中
if (root)
QueuePush(&q, root);
//往后走,到NULL则退出,取完数据
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* front1 = QueueFront(&q);
QueuePop(&q);
//判断队头数据是否为空,为空则继续走,不为空则输出false
if (front1)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
// 二叉树叶子节点个数
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 main()
{
BTNode* root = CreatBinaryTree();
PreOrder(root);
printf("\n");
InOrder(root);
printf("\n");
PostOrder(root);
printf("\n");
int size = TreeSize(root);
printf("TreeSize:%d\n", size);
int size1 = TreeSize(root);
printf("TreeSize:%d\n", size1);
int height = TreeHeight(root);
printf("HeightSize:%d\n", height);
int K1 = BinaryTreeLevelKSize(root, 3);//层数
printf("BinaryTreeLevelKSize:%d\n", K1);
int K2 = BinaryTreeLevelKSize(root, 2);//层数
printf("BinaryTreeLevelKSize:%d\n", K2);
printf("地址为:%p\n", BinaryTreeFind(root, 4));
printf("地址为:%p\n", BinaryTreeFind(root, 50));
printf("LevelOrder:");
LevelOrder(root);
printf("判断是否为完全二叉树:");
printf("%d\n", BinaryTreeComplete(root));
printf("该二叉树的叶子结点为:");
printf("%d", BinaryTreeLeafSize(root));
BinaryTreeDestory(root);
return 0;
}
总结
二叉树看起来很简单,实际上有很多的妙用,利用二叉树的性质我们可以学习到很多不同的方法去练习题目,虽然二叉树在现实中不经常使用,但是思想我们需要记牢,代码要练会练熟。
家人们不要忘记点赞收藏+关注哦!!!