当前位置:网站首页>二叉树的基本操作
二叉树的基本操作
2022-07-01 21:36:00 【51CTO】
图6所示的二叉树中,每个结点含一个字符,其非终端结点都是运算符,终端结点都是操作数。请编写程序,实现对该二叉树的基本操作。具体操作如表2所示。请自行编写主程序对各方法进行测试。
图6 一棵二叉树T
(1)按先序序列,建立二叉链表:编写程序,接收T的先序遍历序列string,根据string,建立T的二叉链表:btnode *creatTree(char *string): 其中,btnode为二叉链表的结点结构;string为T的先序遍历序列。
在输入的先序序列字符串string中,我们使用#来代替为NULL的树结点,如上图中a的左孩子和右孩子就用#代表,按照这个规则,先序遍历上图的树得到的string就为:
这也是我们创建树时要传递的字符串。
二叉树我们除了有存储当前树结点值的val外,还需要两个指针left,right,分别指向该根节点的左孩子和右孩子。这里我们用的是孩子双亲表示法,也可以使用其他的表示方法,如孩子兄弟表示法,left指向当前树结点的孩子结点,right指向当前树结点右边的兄弟结点。
树结点结构体为:
接着是按照输入的字符串创建二叉链表,因为每一个树结点都需要存储一个运算符或者运算数,所以使用全局变量index作为string移动的下标。传入的字符串是按照先序序列输入的,我们创建树的时候也按照先序创建。对于字符串中每一个不为#号的字符,开辟树结点空间,存储string[index]字符,同时递归其左子树和右子树,每次递归的时候都需要令index递增。
递归结束的条件是遇到的#字符,简单模拟一下创建树的过程:
输入字符串为:-+a##*b##-c##d##/e##f##
1,遇到-,开辟树结点,存储-,递归创建其左子树和右子树
2,进入创建其左子树的过程中
3,遇到+,开辟树结点,存储+,递归创建其左子树和右子树
4,遇到a,开辟树结点,存储a,递归创建其左子树和右子树
5,遇到#,故a的左子树为NULL,进入创建a的右子树的递归中
6,遇到#,故a的右子树为NULL,进入创建+的右子树递归中
7,遇到*,开辟树结点,存储*,递归创建其左子树和右子树
......
等等
最后返回最开始的根结点,即-的树结点root
如上,就可以创建出一棵按照输入的先序序列字符串顺序的二叉树
创建过程代码为:
#define LEN sizeof(btnode)
int index=0;
btnode* creatTree(char* string){
char temp=*(string+index++);
btnode* root=NULL;
if(temp=='#'){
root=NULL;
}else{
root=(btnode*)malloc(LEN);
root->val=temp;
root->left=creatTree(string);
root->right=creatTree(string);
}
return root;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
(2)二叉树遍历:编写程序,对T进行先序遍历、中序遍历、后序遍历
voidpreorder(btnode *T): 先序遍历T,输出其先序序列
voidinorder(btnode *T): 中序遍历T,输出其中序序列
voidpostorder(btnode *T) : 后序遍历T,输出其后序序列
先序遍历,中序遍历,后续遍历,使用递归实现的话,思路上是一致的。
从先序遍历上说,想要输出一棵二叉树的先序序列,我们需要知道先序序列的含义,即先输出根节点,输出左子树,再输出右子树。对于其左子树,也是先输出左子树的根节点,左子树的左子树,再输出左子树的右子树。递归点出现,我们只需要按照这个规则递归即可,当当前树结点为空时,就不再继续递归该子树。当树根结点的右子树也递归结束后,就代表整棵树的先序遍历结束了。
先序遍历代码如下:(对于#因为其是NULL结点,我们就不再输出了)
可以看到,输出的时候,也是按照刚才我们所说的规则输出,中序遍历和后序遍历跟其类似,只需要改变输出当前树结点值的位置即可。
中序遍历:
后序遍历:
(3)求二叉树的叶子数:编写程序,求T的叶子数
intcountLeaf(btnode *T);
求二叉树的叶子数,我们也使用递归的方式,想要求得一棵树的叶子数量,即判断当前树结点是否为叶子节点,是的话叶子节点数目+1,否则递归计算其左子树和右子树上叶子数的和。
是用静态局部变量存储叶子数
(4)求二叉树的深度:编写程序,求T的深度
inthigh(btnode *T):编写程序,求T的深度
还是递归的思想,很多问题使用递归解决,就会变得很简单
想要求一棵树的深度,即求其左子树和右子树中,最大的深度+1,+1的原因即算上当前树结点的深度为1
(5)输出带括号的中缀表达式:如果对T进行中序遍历,将得到一个中缀表达式,但该表达式没有小括号,无法体现运算的优先级。请编写程序,对二叉树T,输出其带小括号的中缀表达式。
voidinfixExpression(btnode *T);
可以发现,我们中序遍历二叉树时,结果为:

而在树的结构中,很明显是c-d,但是在输出的结果表达式中却体现不出这一点,这道题的目的就是让我们添加括号体现优先级。
类似于逆波兰式中运算符优先级的判定,考虑什么情况下我们需要添加括号,实际上只有当前树结点的运算符优先级大于其左子树或右子树根节点的运算符优先级时,才需要添加括号。而什么时候当前树结点的运算符优先级大于其左子树或右子树的根节点运算符优先级,其实只有一种情况,即当前树结点为乘号或者除号,而左子树或右子树为减号或者加号,这时候才需要添加括号。即便左子树或右子树不是运算符,也在上述判断中不满足条件,也就是不添加括号。
使用check函数进行优先级判定
当根结点运算符优先级高于左子树或右子树根结点运算符时,返回true,添加括号
因为是在中序遍历的基础上添加括号,所以我们也在中序遍历代码的基础上进行修改
代码为:
void infixExpression(btnode* T){
btnode* temp=T;
if(temp==NULL){
// printf("#");
return ;
}else{
if(temp->left){
bool flag=check(temp->left->val,temp->val);
if(flag){
printf("(");
}
infixExpression(temp->left);
if(flag){
printf(")");
}
}
printf("%c",temp->val);
if(temp->right){
bool flag=check(temp->right->val,temp->val);
if(flag){
printf("(");
}
infixExpression(temp->right);
if(flag){
printf(")");
}
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
判定每一个树结点与其左右孩子的根节点的优先级,满足条件时输出左括号,递归其左子树,递归结束后再输出右括号。其余过程与中序遍历差别不大。
完整代码为:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct btnode{
char val;
struct btnode *left,*right;
}bttree;
#define LEN sizeof(btnode)
int max(int a,int b){
return a>b?a:b;
}
void preorder(btnode* T){
btnode* temp=T;
if(temp==NULL){
// printf("#");
return ;
}else{
printf("%c",temp->val);
preorder(temp->left);
preorder(temp->right);
}
}
void inorder(btnode* T){
btnode* temp=T;
if(temp==NULL){
// printf("#");
return ;
}else{
inorder(temp->left);
printf("%c",temp->val);
inorder(temp->right);
}
}
void postorder(btnode* T){
btnode* temp=T;
if(temp==NULL){
// printf("#");
return ;
}else{
postorder(temp->left);
postorder(temp->right);
printf("%c",temp->val);
}
}
int countLeaf(btnode* T){
btnode* temp=T;
static int count=0;
if(temp&&temp->left==NULL&&temp->right==NULL){
count++;
return 0;
}else{
countLeaf(temp->left);
countLeaf(temp->right);
}
return count;
}
//求树的深度,即求左子树和右子树中的最大深度,出现递归点
int high(btnode* T){
btnode* temp=T;
if(temp==NULL){
return 0;
}else{
return max(high(temp->left)+1,high(temp->right)+1);
}
}
bool check(char a,char b){
if((a=='+'||a=='-')&&(b=='*'||b=='/')){
return true;
}else{
return false;
}
}
//输出带括号的中缀表达式,当根结点运算符优先级高于左子树或右子树根结点运算符时,相应左或右子树前就需要加括号
void infixExpression(btnode* T){
btnode* temp=T;
if(temp==NULL){
// printf("#");
return ;
}else{
if(temp->left){
bool flag=check(temp->left->val,temp->val);
if(flag){
printf("(");
}
infixExpression(temp->left);
if(flag){
printf(")");
}
}
printf("%c",temp->val);
if(temp->right){
bool flag=check(temp->right->val,temp->val);
if(flag){
printf("(");
}
infixExpression(temp->right);
if(flag){
printf(")");
}
}
}
}
int index=0;
btnode* creatTree(char* string){
char temp=*(string+index++);
btnode* root=NULL;
if(temp=='#'){
root=NULL;
}else{
root=(btnode*)malloc(LEN);
root->val=temp;
root->left=creatTree(string);
root->right=creatTree(string);
}
return root;
}
int main(){
btnode* root=creatTree("-+a##*b##-c##d##/e##f##");
printf("输入字符串为:-+a##*b##-c##d##/e##f##\n");
printf("先序遍历结果为:");
preorder(root);
printf("\n中序遍历结果为:");
inorder(root);
printf("\n后序遍历结果为:");
postorder(root);
printf("\n二叉树的叶子数为:%d\n",countLeaf(root));
printf("二叉树的深度为:%d\n",high(root));
printf("带括号的中缀表达式为:");
infixExpression(root);
free(root);
return 0;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
在二叉树的操作中,递归是经常会用到也是很重要的思想,需要熟练掌握应用,以及举一反三。
以上
边栏推荐
- 《QTreeView+QAbstractItemModel自定义模型》:系列教程之三[通俗易懂]
- TOPS,处理器运算能力单位、每秒钟可进行一万亿次
- 《軟件工程導論(第六版)》 張海藩 複習筆記
- PCB plug hole technology~
- 升级版手机检测微信工具小程序源码-支持多种流量主模式
- 4. 对象映射 - Mapping.Mapstercover
- K-means based user portrait clustering model
- 【深度学习】利用深度学习监控女朋友的微信聊天?
- 【mysql 07】GPG key retrieval failed: “Couldn‘t open file /etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2022“
- [deep learning] use deep learning to monitor your girlfriend's wechat chat?
猜你喜欢
随机推荐
“丝路正青春 风采看福建”在闽外籍青年短视频大赛火热征集作品中
Kuberntes云原生实战一 高可用部署架构
CNN卷积神经网络原理讲解+图片识别应用(附源码)[通俗易懂]
个人炒股怎样开户?安全吗。
JS how to get a list of elements in a collection object
leetcode刷题:栈与队列02(用队列实现栈)
【商业终端仿真解决方案】上海道宁为您带来Georgia介绍、试用、教程
[deep learning] use deep learning to monitor your girlfriend's wechat chat?
微信小程序,连续播放多段视频。合成一个视频的样子,自定义视频进度条
php反射型xss,反射型XSS测试及修复
2022熔化焊接与热切割上岗证题目模拟考试平台操作
多个张量与多个卷积核做卷积运算的输出结果
Detailed explanation and code example of affinity propagation clustering calculation formula based on graph
TOPS,处理器运算能力单位、每秒钟可进行一万亿次
新版图解网络PDF即将发布
《軟件工程導論(第六版)》 張海藩 複習筆記
名单揭晓 | 2021年度中国杰出知识产权服务团队
基础—io密集型计算和cpu密集型计算
BPR(贝叶斯个性化排序)
杰理之蓝牙耳机品控和生产技巧【篇】








