本文讲解了链式二叉树的前、中、后序遍历,层序遍历,并附了有关链式二叉树的一些经典问题及详解,提高大家对链式二叉树的学习深度,并学会用分治的思想解决一些问题。
建议阅读本文之前阅读一下有关二叉树的基本知识。
普通二叉树的增删查改没有价值,如果为了单纯储存数据不如使用线性表,这里的学习是为了更好的控制它的结构,为后续学习更复杂的搜素二叉树,AVL数树,红黑树等打基础,而且很多二叉树OJ算法题都出在普通二叉树上。
在学习链式二叉树的基本操作前,需先要创建一棵链式二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
BTNode* BuyNode(int data)
{
BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
if (newNode == NULL)
{
printf("malloc fail");
exit(-1);
}
newNode->_data = data;
newNode->_left = newNode->_right = NULL;
return newNode;
}
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);
node1->_left = node2;
node1->_right = node4;
node2->_left = node3;
node4->_left = node5;
node4->_right = node6;
return node1;
}
注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。
在讲解链式二叉树之前,我们先浅浅回忆一下二叉树的概念:
二叉树是树的一种特例,该树由一个根节点加上两棵分别称为左子树和右子树的二叉树组成,两棵子树又可看成是一个根节点和属于它的两棵子树组成的二叉树,以此递归下去,直到子树为空。
这种递归的思想对于二叉树尤为重要,在之后的讲解中我你们会反复遇到类似这样的概念。
学习二叉树的结构,最简单的方式就是遍历。所谓二叉树的遍历,就是按照某种特定的规则,依次对而二叉树中的节点进行相应的操作,并且每个节点只操作一次。 访问节点所做的操作依赖于具体的应用问题。遍历是二叉树最重要的运算之一,也是二叉树上其他运算的基础。
下图是遍历二叉树的一般递归路径,递归路径虽然相同,但由于递归过程中一个节点可能经过多次,是一进节点就访问、归回来的时候的访问、还是向上返回的时候再访问,不同的访问时序也会也会产生不同的结果
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:(这里的前、中、后都是说当前树的根节点进行访问的时序,且子树都是先左后右访问)
由于真正能直接读取的只有某子树的根,所以这里的访问左子树,右子树又可解释为:访问以左节点、右节点为根的树,以此向下递归。
// 二叉树前序遍历
void PreOrder(BTNode* root);
// 二叉树中序遍历
void InOrder(BTNode* root);
// 二叉树后序遍历
void PostOrder(BTNode* root);
接下来,我们直接用打印的方式来代表节点的访问,实现一下三种遍历方式,并让大家感受一下不同结构数据的访问顺序,最后我们使用函数递归图对前序遍历进行解释,直观感受一下函数递归的过程。
通过定义,写出代码相对容易,但实际的递归过程却相对难理解。这里,把前序遍历的代码呈现给大家,以便后续讲解。
void PreOrder(BTNode* root)
{
if (root == NULL);//如果树为空,打印空,不再向下
{
printf("NULL ");
return;
}
printf("%d ", root->_data)//访问当前根节点
PreOrder(root->_left);//访问左子树
PreOrder(root->_right);//访问右子树
}
首先,我们研究一下前序遍历到底会以什么次序进行输出(这里我们将空指针也进行输出,以便理解)
最终直到所有叶(NULL)都被访问,这棵树就遍历结束了。
上图从左至右的值,就是递归过程中访问的次序。
我们看一下运行结果:
再上一道例题巩固一下:
如果一颗二叉树的前序遍历的结果是ABCD,则满足条件的不同的二叉树有( )种
A.13
B.14
C.15
D.16
首先思考一下,ABCD四个节点可以生成的二叉树的高度可以是多少?
高度最低也就是完全二叉树的情况,也就是高度位3;
高度最高也就是单边树(每一层只有一个节点)的情况,高度位4;
接下来就分别对这两种情况进行分析:
上面两种情况一共是6+8 = 14种组合,选B。
这就是一段完整的前序遍历的递归过程
有了前序遍历的讲解,中序遍历和后序遍历也基本相同,这里我们就简单展示一下访问的顺序和代码,至于递归过程,基本与前序相同,仅仅是访问根节点数据区的时间不同,大家可以自己画图感受一下。
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);
}
最后留一个中序遍历的访问顺序图给大家自己实现以下,可以对照以下运行结果哦。
- 已知某二叉树的中序遍历序列为JGDHKBAELIMCF,后序遍历序列为JGKHDBLMIEFCA,则其前序遍历序列为( )
A.ABDGHJKCEFILM
B.ABDGJHKCEILMF
C.ABDHKGJCEILMF
D.ABDGJHKCEIMLF
解析:
根为: A
A的左子树:JGDHKB ; A的右子树:ELIMCF
A的左子树的根:B ; A的右子树的根:C
B的左子树:JGDHK ; B的右子树:空;; C的左子树:ELIM ;C的右子树:F
B的左子树的根:D ; C的左子树根:E
D的左子树的根:G ;D的右子树的根:H ;E的右子树的根:I
故树的结构为:
A
B C
D E F
G H I
J K L M
故前序遍历:
A B D G J H K C E I L M F
2.已知某二叉树的前序遍历序列为ABDEC,中序遍历序列为BDEAC,则该二叉树( )
A.是满二叉树
B.是完全二叉树,不是满二叉树
C.不是完全二叉树
D.是所有的结点都没有右子树的二叉树
与上一题大致相同,只不过这回前序遍历的首元素是根节点
相同思路可以确定树的结构:
A
B C
D
E
所以既不是满二叉树,也不是完全二叉树
之前的前中后序都是对节点的访问,这里,我们依然是使用这三种方式进行节点个数的计算,并逐步理解分治的思想。
// 二叉树节点个数
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 BinaryTreeSize(BTNode* root,int* counter)
{
if (root == NULL)//空树返回大小为0
{
return ;
}
(*counter)++;
BinaryTreeSize(root->left, counter);
BinaryTreeSize(root->right, counter);
}
这里有个小问题:这里的counter可以是int型吗?
答案是不可以,如果是int型,那么传值传的counter都会有一个临时拷贝,给这个临时拷贝++后,实际的counter值并未改变,所以必须使用传址的方式。
在讲解方法2之前,我们先想象一个场景:
一天校长想统计这个学校一共有多少人,这时,他把所有的院长都叫过来,让他们上报自己学院有多少人,院长下去了又把所有辅导员叫过来询问,辅导员又叫来了所有班长,班长回去又叫了各个社长,社长统计后上报班长,班长上报辅导员,层层上报,最终校长就知道这个学校有多少人了。
其实数二叉树节点个数就是这个原理:
头节点获得左右子树的节点个数,再加上自己,就是整个二叉树的大小了。这里的子树也是相同的获取原则,直至出现一颗空树,空树就上报0.
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)//空树返回大小为0
{
return 0;
}
//返回左子树大小+右子树大小+自己1
return BinaryTreeSize(root->left) +
BinaryTreeSize(root->right) + 1;
}
由于版本1的参数更多,操作更复杂,使用方式二更易理解,所以建议使用方式二
所谓叶子节点就是含有子树个数为0的节点,也就是左节点和右节点都是NULL。与计算总节点的个数相似,只不过这里又有遇到子节点才+1.同样这里也可以用计数器和分治两种方式解决,但我只展示以下分治的方式,计数器大家如果感兴趣可以自己实现。
int BTreeLeafSize(BTNode* root)
{
//空节点
if (root == NULL)
{
return 0;
}
//叶子节点
if (root->_left == NULL && root->_right == NULL)
{
return 1;
}
//返回左右子树的叶子节点个数
return BTreeLeafSize(root->_right) + BTreeLeafSize(root->_left);
}
返回第K层的节点个数,可以看成是,返回两个子树的”K-1层的节点个数“,子树的子树又是”K-2层的节点个数“以这种方式递归下去,一直到"返回第1层的节点的个数",这时返回1就好,因为任何二叉树的第一层只有一个节点,当然如果在k减到1之前,提前递归到了NULL节点,那返回0即可,表示这个分支下没有第K层节点呗。
int BinaryTreeLeafSize(BTNode* root, int k)
{
assert(k >= 1);
if (root == NULL)
{
return 0;
}
if (k == 1)//递归到这棵树是,刚好是求第一层的节点数
{
return 1;
}
//递归到子树就是求k-1层的节点数,左子树加右子树就是总共的第k层的节点数
return BinaryTreeLeafSize(root->_left, k - 1) + BinaryTreeLeafSize(root->_right, k - 1);
}
这样一棵二叉树的最大深度就是4,即ABEH这一条线路最长
这样一个问题可以归结成是:比出左右子树中深度更大的那个的深度,加自己的1深度就是总深度
总结成分支问题就是:返回左右子树深度较大的+1;
同样遇到空数返回深度为0;
int BTreeDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int leftDepth = BTreeDepth(root->_left);
int rightDepth = BTreeDepth(root->_right);
return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
在二叉树查找值为x的节点,返回次节点的地址,若找不到返回NULL
查找过程无非就是在遍历过程中寻找值为x的节点,找到就返回这个节点的地址,至于前中后序都可以
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->val == x)//找到就返回
{
return root;
}
BTNode* leftTree = BinaryTreeFind(root->left, x);//左树是否找到
if (leftTree != NULL)//找到了返回找到的节点
{
return leftTree;
}
BTNode* rightTree = BinaryTreeFind(root->right, x);//右树是否找到
if (rightTree != NULL)//找到了返回找到的节点
{
return rightTree;
}
return NULL;//当前节点、左树、右树都没找到就返回空
}
如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
只有给定的树是单值二叉树时,才返回 true;否则返回 false。
由于oj给定的函数是只有一个根节点的,但我们这里需要一个参数放头节点的值以便后序所有节点去和他比较,所以设置一个辅助函数,让原函数去调用。像这样:
bool _isUnivalTree(struct TreeNode* root,int val)
{
}
bool isUnivalTree(struct TreeNode* root)
{
return _isUnivalTree(root, root->val);
}
有了这个比较值就相对好写:
返回”根节点==val&&左树是单值数&&右树也是单值数“即可
bool _isUnivalTree(struct TreeNode* root,int val)
{
if (root == NULL)
{
return true;
}
if (root->val != val)
{
return false;
}
return _isUnivalTree(root->left,val) && _isUnivalTree(root->right,val);
}
bool isUnivalTree(struct TreeNode* root)
{
return _isUnivalTree(root, root->val);
}
分析:
如果要带比较值,还要写辅助函数,相对麻烦,我们是不是可以直接让根节点和左右进行比较。
bool isUnivalTree(struct TreeNode* root)
{
if (root == NULL)
{
return true;
}
if (root->left && root->left->val != root->val)
{
return false;
}
if (root->right && root->right->val != root->val)
{
return false;
}
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
这道题用分治的思想也很好解决。
两棵树相同,无非就是:根节点相同,两棵树的左树相同,两棵树的右树相同就返回真。
宏观思想解决了,那我们来看看递归的终止条件,盲猜递归到空节点就返回,我们分析一下细节:
1.递归到左树右树都为真,无疑返回真
2.要是只有一棵树为空,节点数都不同,两棵树一定不同,返回假
看一下代码:
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if (p || q)//不全是空
{
if (p && q)//都不空
{
return (p->val == q->val) && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
else//一棵空一棵不空
{
return 0;
}
}
else//全空
{
return 1;
}
}
要求判断是否为对称二叉树,根节点毫无作用,唯一需要判断的就是左右子树是否对称或者说镜像。
为了递归方便,我们干脆定义一个辅助函数,传入左树和右树
bool treeSymmCmp(TreeNode* p, TreeNode* q)
{
}
bool isSymmetric(struct TreeNode* root)
{
struct TreeNode* treeLeft = root->left;//左子树
struct TreeNode* treeRight = root->right;//右子树
return treeSymmCmp(treeLeft, treeRight);//调用辅助函数
}
接下来研究一下这个辅助函数怎么写:
与上一题相似,判断p、q两棵树是否对称不可以看成:
如果还不能确保,不妨试一下子问题是否还符合这样的判断方式:
如下这棵对称树
摘取左右树:
我们发现,第二层递归与第一层递归的判断方式可以完全重叠,那么宏观的递归逻辑就对了
接下来看看微观的结束条件:
看第二层的第3行可知,两树都为空就返回真,
若两棵树只有一颗不是空,那么返回假(如果4不为空,5为空,那么一定不对称)
我们实现一下:
bool treeSymmCmp(TreeNode* p, TreeNode* q)
{
if (p || q)
{
if (p && q)
{
return (p->val == q->val) && treeSymmCmp(p->left, q->right) && treeSymmCmp(p->right, q->left);
}
else
{
return 0;
}
}
else
{
return 1;
}
}
bool isSymmetric(struct TreeNode* root)
{
struct TreeNode* treeLeft = root->left;
struct TreeNode* treeRight = root->right;
return treeSymmCmp(treeLeft, treeRight);
}
与之前写的遍历基本相同,只不过这里不是打印,而是拷贝到数组中,然后返回整个数组。
int BTreeSize(struct TreeNode* root)
{
if (root == NULL)
{
return 0;
}
return BTreeSize(root->left) + BTreeSize(root->right) + 1;
}
void PrevOrder(struct TreeNode* root, int* arr, int* num)
{
if (root == NULL)
{
return;
}
arr[*num] = root->val;
++(*num);
PrevOrder(root->left, arr, num);
PrevOrder(root->right, arr, num);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
*returnSize = BTreeSize(root);
int* arr = (int*)malloc(sizeof(int) * *returnSize);
int size = 0;
PrevOrder(root, arr, &size);
return arr;
}
二叉树的中序遍历
二叉树的后序遍历
与前序基本相似,这里直接上答案:
int TreeSize(struct TreeNode* root)
{
if (root == NULL)
{
return 0;
}
return TreeSize(root->left) + TreeSize(root->right)+1;
}
void _inorderTraversal(struct TreeNode* root, int* a, int* i)
{
if (root == NULL)
{
return;
}
_inorderTraversal(root->left, a, i);
a[*i] = root->val;
(*i)++;
_inorderTraversal(root->right, a, i);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize)
{
int treeSize = TreeSize(root);
int* ret = (int*)malloc(sizeof(int) * treeSize);
int i = 0;
_inorderTraversal(root, ret, &i);
*returnSize = treeSize;
return ret;
}
int TreeSize(struct TreeNode* root)
{
if (root == NULL)
{
return 0;
}
return TreeSize(root->left) + TreeSize(root->right)+1;
}
void _postorderTraversal(struct TreeNode* root, int* a, int* i)
{
if (root == NULL)
{
return;
}
_postorderTraversal(root->left, a, i);
_postorderTraversal(root->right, a, i);
a[*i] = root->val;
(*i)++;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize)
{
int treeSize = TreeSize(root);
int* ret = (int*)malloc(sizeof(int) * treeSize);
int i = 0;
_postorderTraversal(root, ret, &i);
*returnSize = treeSize;
return ret;
}
这里放这道题,仅仅是为了熟悉链式二叉树的数据结构,这里就先没必要涉及一些优化的算法,有兴趣可以看一下LeeTcode官方的讲解,我们这里讲解一下暴力算法就好。
代码:
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if (p || q)
{
if (p && q)
{
return (p->val == q->val) && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
else
{
return 0;
}
}
else
{
return 1;
}
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
if (root == NULL)//探到底都没有,返回假
{
return false;
}
if (isSameTree(root, subRoot))//前序遍历是否有相同
{
return true;
}
bool judLeft = isSubtree(root->left, subRoot);//判断左树
bool judRight = isSubtree(root->right, subRoot);//判断右树
return judLeft || judRight;//若左右任意一棵树是,也返回真
}
代码:
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if (p || q)
{
if (p && q)
{
return (p->val == q->val) && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
else
{
return false;
}
}
else
{
return true;
}
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
if (root == NULL)
{
return false;
}
else
{
return isSameTree(root,subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
}
前面的前、中、后序遍历,包括所有的例题属于深度有限遍历,通过递归的方式,按照箭头顺序,经历如下的遍历。
这里我们再来看一下二叉树的层序遍历,即如下一层一层的遍历方式:
如何实现呢?
当走到一个节点,只能访问到三部分,当前节点,左树,右树
但也不可能左右同时进行,所以必须对节点进行存储,访问存好的节点:
以此类推,直至访问到空
我们可以看到存储过程中,进入的顺序是A、B、C、D、E……,访问的顺序也是A、B、C、D、E……,也就是先进先出,这时大家想到了哪种数据结构?
诶,对!
队列
所以我们使用队列的结构来辅助实现,大家可以从如下的链接中找到链表的结构和c语言实现,下面会直接调用
数据结构——队列
观察上述原理中存储和访问的过程中,只有第一行是只进行了存储的,后面都是先访问,再存子节点,所以毫无疑问,除了第一句,剩余循环实现就好。
接下来再思考这样一个问题:有必要一次把一层都遍历完吗?
不妨试一试每次只对一个节点进行访问,同时带入它的两个子节点:
循环什么时候结束呢?
最一开始,队列就有一个元素(根节点),之后只要访问的节点还有子节点,那队列就一直有元素输入,直至队列中所有元素都没了子节点,再把队列访问完,也就结束了整个层序遍历过程。
所以结束条件就是队列为空。
void LevelOrder(BTNode* root)
{
//创建队列
Queue q;
QueueInit(&q);
if (root)//避免传入了空树
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))//结束条件——队列为空
{
BTNode* ret = QueueFront(&q);
QueuePop(&q);
printf("%d ", ret->data);//访问
if (ret->left)//有左节点,入队列
{
QueuePush(&q, ret->left);
}
if (ret->right)//有右节点,入队列
{
QueuePush(&q, ret->right);
}
}
QueueDestroy(&q);//销毁队列
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);
![完全二叉树——一般二叉树](https://img-blog.csdnimg.cn/98816bf27bc94ec3ac5d2e24efa2b9d3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWFzc2FjaHVzZXR0c18xMQ==,size_16,color_FFFFFF,t_70,g_se,x_16
观察上图,我们发现,所谓完全二叉树,无非就是在整段层序遍历过程,不在中间出现任意一个空隙。
那么,怎么实现呢?
这里我们讨论一个问题:
有没有这种可能:在出现空节点时,右侧的非空节点还没来的及入队列,导致无法正确判断出结果?
答案是不可能的,观察上面的一般二叉树,当层序遍历到空节点时,则上一层的节点一定是全部访问,访问的同时他们也将下一层的全部节点都带出来了,所以一定不可能出现这种情况。
bool BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root==NULL)//空树返回假
{
return false;
}
QueuePush(&q, root);//根节点入队列
BTNode* nodeInQ = root;
while (nodeInQ)//循环找到队列中的空节点
{
QueuePush(&q, nodeInQ->left);
QueuePush(&q, nodeInQ->right);
QueuePop(&q);
nodeInQ = QueueFront(&q);
}
while (!QueueEmpty(&q))//遍历剩余队列
{
nodeInQ = QueueFront(&q);
QueuePop(&q);
if (nodeInQ)//再次出现非空,不是完全二叉树
{
return false;
}
}
QueueDestroy(&q);
return true;//未出现非空,就是完全二叉树
}
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
题目中给出的“ABC##DE#G##F###”这个字符串实际上就是对一个二叉树进行前序遍历后的输出结果,我们需要凭借这个字符串,将二叉树还原出来,然后再对还原出来的二叉树进行中序遍历输出。至于输出直接调用前面的代码就好,我们这里主要分析一下它的创建过程。
如何前序创建一棵树呢?
在讲解创建之前,先让我们回忆一下先前访问的前序遍历:
这时,我们仿照访问的方式,写一个创建试试
函数怎么构造?
BTNode* BinaryTreeCreate(BTDataType* a, int* i);
//a——字符串,n——字符串长度,pi——当前字符下标
注意:这里的 i 必须是指针。如果传int,那么 i 就是个临时变量,生命周期只在当前函数,也就是说对 i 的修改只在当前函数有效,当递归返回时,这一层函数的 i还是指向前面位置的;如果使用指针,则每次修改的就都是同一个 i 了。
//二叉树结构
typedef char BTDataType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BTDataType data;
}BTNode;
//创建二叉树
BTNode* CreatBinaryTree(char* str,int* i)
{
if (str[*i] == '#')
{
(*i)++;
return NULL;
}
if (str[*i] == '\0')
{
return NULL;
}
BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
if (newNode == NULL)
{
printf("malloc fail");
exit(-1);
}
newNode->data = str[*i];
(*i)++;
newNode->left = newNode->right = NULL;
newNode->left = CreatBinaryTree(str, i);
newNode->right = CreatBinaryTree(str, i);
return newNode;
}
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
InOrder(root->left);
printf("%c ", root->data);
InOrder(root->right);
}
//测试函数
int main()
{
char str[100] = { 0 };
scanf("%s", str);
int i = 0;
BTNode* tree = CreatBinaryTree(str, &i);
InOrder(tree);
}
void BinaryTreeDestory(BTNode** root);
销毁二叉树,也就是把每个节点都free掉,无非就是遍历一下,只不过这里唯一要注意的一点就是:一定要把左右树都销毁了,再销毁根节点,否则先销毁了根就找不到左右树了。
void BinaryTreeDestory(BTNode** root)//这里传一级也可以,只不过将root置空一下保险一点
{
if (*root == NULL)
{
return;
}
BinaryTreeDestory(&(*root)->left);
BinaryTreeDestory(&(*root)->right);
free(*root);
*root = NULL;
}
笔者书写不易,希望大家点赞支持一下哦,当然三连就更好啦
大家一起努力哇!!!!