问题描述:
表达式可以用表达式二叉树来表示。对于简单的四则运算表达式,请实现以下功能。
(1)对于任意给出的前缀表达式(不带括号)、中缀表达式(可以带括号)或后缀表达式(不带括号),能够在计算机内部构造出一颗表达式二叉树,并且以图示显示出来(字符图或图形的形式)。
(2)对于构造好的内部表达式二叉树、按照用户的要求,输出相应的前缀表达式(不带括号)、中缀表达式(可以带括号,但不允许冗余括号)或后缀表达式(不带括号)
相关知识点:
1.后缀表达式的特点是:一定以两个操作数开始,且以操作符结尾,形如“a b + c d e + * *”就是一个后缀表达式
2.表达式树的特点就是:树的树叶是操作数(常数或变量),而其他节点为操作符。由于一般的操作符都是二元的,所以表达式树一般的都是二叉树。
3.中缀表达式建树过程:代码中InTree()为递归函数、有三个参数、s - 字符串的首地址,i和j分别为子串起始位置和终止位置。如果i == j,说明子串只有一个字符,即为叶子节点,则创建只有一个根节点的二叉树并返回之。如果i != j,根据运算规则(先乘除后加减),在串中找‘+’和‘-’,以最后的‘+’或‘-’为根(体现从左到右的原则);如果没有‘+’或‘-’时,则进一步找‘*’或’/‘(体现先乘除后加减),同样以最后的运算符为根,将串分为两部分,即左子树和右子树。创建一个根节点,将找到的运算符放入,递归调用自身进入左子树的建树过程,之后递归调用自身进入右子树的建树过程。
5.树的遍三种遍历分别是先序遍历、中序遍历与后序遍历,正好对应表达式的三种形式:前缀型、中缀型与后缀型,其中前缀和后缀表达式最大的优点就是不需要括号来表明优先级。
4.1后缀表达式建树过程:我们一次一个符号地读入表达式。如果符号是操作数,那么就建立一个单结点树并将它推入栈中。如果符号是操作符,那么就从栈中弹出两棵树T1和T2(T1先弹出)并形成一棵新的树,该树的根就是操作符,它的左、右儿子分别是T2和T1。然后将指向这颗树的指针压入栈中。
4.2 后缀表达式建树过程图示如下:
(图片来源:https://blog.csdn.net/qq_26849233/article/details/72910010)
如“a b + c d e + * *”生成表达式树的主要过程如下图所示:
(1)依次读入操作数a 和 b,并压入栈中
(2)遇到操作符“+”
![](https://i-blog.csdnimg.cn/blog_migrate/30666f3460e6307ef3b2aa98b68ee94d.png)
(3)遇到c d e 操作数
(4)遇到“+”操作符
(5)遇到“*”操作符
(6)遇到“*”操作符
![](https://i-blog.csdnimg.cn/blog_migrate/b74d7443a8c614e5ba2ba52125ec7e29.png)
完整代码如下:
/* 表达式二叉树 */
//#include "stdafx.h"
//博客地址:https://blog.csdn.net/y_16041527
#include "btree.cpp" //该头文件为我自定义的头文件,其中的函数的声明和实现在我的博客中有
#include <string.h>
//函数声明
bitree InTree(char s[], int i, int j); //中缀表达式建树过程
bitree PosTree(char s[], int len ); //后缀表达式建树过程
bitree PerTree(char s[], int len ); //前缀表达式建树过程
//前缀表达式建树过程 - 由于操作数在叶节点位置和前缀表达式的特点,其建树过程与后缀表达式一致,只不过扫描方向正好相反
bitree PerTree(char s[], int len)
{
// cout << "len:" << len << endl;
//计数变量
int i;
bitree p, root;
//临时栈 - 用来存放指向树节点的指针
struct stack
{
bitnode *vec[MaxSize];
int top;
} ;
struct stack q;
q.top = 0;
//遍历字符串,若为操作数 - 则生成根节点并将指向该根节点的指针入栈、若为运算符 - 则生成节点并在临时栈中弹出两个指向操作数
//节点的指针,并指向该运算符节点并将其入栈
for( i = len-1; i >= 0; i-- )
{
//为操作符
if( s[i] == '+' || s[i] == '/' || s[i] == '*' || s[i] == '-' )
{
p = (bitree)malloc(sizeof(bitnode));
p->data = s[i];
p->left = q.vec[q.top--]; //先弹出的为左节点
p->right = q.vec[q.top--]; //后弹出的为右节点
q.vec[++q.top] = p; //将根节点入栈
}
else
{
//s[i]为操作数
p = (bitree)malloc(sizeof(bitnode));
p->data = s[i];
p->left = NULL;
p->right = NULL;
q.vec[++q.top] = p; //将指向操作数节点的指针入栈
}
}
root = q.vec[q.top--]; //这一步很关键,因为该二叉树的根节点最后被保留在了栈中
return root;
}
//中缀表达式建树过程 - 递归过程
bitree InTree(char s[], int i, int j) //s - 表达式字符串、i - 字符串起始位置、j - 字符串最后一个字符的位置
{
//动态生成的树节点
bitree p;
int k, flag = 0, pos;
//如果i == j,则说明字符串只有一个字符,即为叶子节点、则创建只有一个根节点的二叉树并返回
if (i == j)
{
p = (bitree)malloc(sizeof(bitnode));
p->data = s[i];
p->left = NULL;
p->right = NULL;
return p;
}
//以下是 i != j的情况
//从左往右找最后一个+或-,先找+或-为了体现先乘除后加减的原则
for (k = i; k <= j; k++)
{
if (s[k] == '+' || s[k] == '-')
{
flag = 1;
pos = k;
}
}
//若没有+或-,则寻找字符串中最后一个*或/
if (flag == 0)
{
for (k = 0; k <= j; k++)
{
if (s[k] == '*' || s[k] == '/')
{
flag = 1;
pos = k;
}
}
}
//若flag不等于0,则以pos为界将字符串分为左右两部分,分别对应表达式二叉树的左、右子树
//同样以最后的运算符为根,将串分为两部分
//创建一个根节点、将找到的运算符放入
if (flag != 0)
{
p = (bitree)malloc(sizeof(bitnode));
p->data = s[pos];
p->left = InTree(s, i, pos - 1); //递归调用自身进入其左子树建树过程
p->right = InTree(s, pos + 1, j); //递归调用自身进入其右子树建树过程
return p;
}
else
return NULL;
}
//后缀表达式建树过程
bitree PosTree(char s[], int len)
{
// cout << "len:" << len << endl;
//计数变量
int i;
bitree p, root;
//临时栈 - 用来存放指向树节点的指针
struct stack
{
bitnode *vec[MaxSize];
int top;
} ;
struct stack q;
q.top = 0;
//遍历字符串,若为操作数 - 则生成根节点并将指向该根节点的指针入栈、若为运算符 - 则生成节点并在临时栈中弹出两个指向操作数
//节点的指针,并指向该运算符节点并将其入栈
for( i = 0; i < 9; i++ )
{
//为操作符
if( s[i] == '+' || s[i] == '/' || s[i] == '*' || s[i] == '-' )
{
p = (bitree)malloc(sizeof(bitnode));
p->data = s[i];
p->right = q.vec[q.top--]; //先弹出的为右节点
p->left = q.vec[q.top--]; //后弹出的为左节点
q.vec[++q.top] = p; //将根节点入栈
}
else
{
//s[i]为操作数
p = (bitree)malloc(sizeof(bitnode));
p->data = s[i];
p->left = NULL;
p->right = NULL;
q.vec[++q.top] = p; //将指向操作数节点的指针入栈
}
}
root = q.vec[q.top--]; //这一步很关键,因为该二叉树的根节点最后被保留在了栈中
return root;
}
int main()
{
bitree root;
char s[MaxSize];
cout << "中缀表达式:";
cin >> s;
root = InTree(s, 0, strlen(s) - 1);
cout << "表达式二叉树" << endl;
DispTree(root);
cout << endl;
cout << "该表达式二叉树三种遍历" << endl;
cout << "前序遍历:";
PerOrder(root);
cout << endl;
cout << "中序遍历:";
InOrder(root);
cout << endl;
cout << "后序遍历:";
PostOrder(root);
cout << endl;
cout << "后缀表达式:";
cin >> s;
root = PosTree(s,strlen(s));
cout << "表达式二叉树" << endl;
DispTree(root);
cout << endl;
cout << "前缀表达式:";
cin >> s;
root = PerTree(s,strlen(s));
cout << "表达式二叉树" << endl;
DispTree(root);
cout << endl;
root = FreeTree(root);
if (root == NULL)
cout << "释放成功" << endl;
return 0;
}
运行结果:
中缀表达式:a+b*c-e/f
表达式二叉树
- (r)------------------
+ (0)----------------
a (0)--------------
* (1)--------------
b (0)------------
c (1)------------
/ (1)----------------
e (0)--------------
f (1)--------------
表达式二叉树
- (r)------------------
+ (0)----------------
a (0)--------------
* (1)--------------
b (0)------------
c (1)------------
/ (1)----------------
e (0)--------------
f (1)--------------
该表达式二叉树三种遍历
前序遍历:- + a * b c / e f
中序遍历:a + b * c - e / f
后序遍历:a b c * + e f / -
后缀表达式:abc*+ef/-
表达式二叉树
- (r)------------------
+ (0)----------------
a (0)--------------
* (1)--------------
b (0)------------
c (1)------------
/ (1)----------------
e (0)--------------
f (1)--------------
前序遍历:- + a * b c / e f
中序遍历:a + b * c - e / f
后序遍历:a b c * + e f / -
后缀表达式:abc*+ef/-
表达式二叉树
- (r)------------------
+ (0)----------------
a (0)--------------
* (1)--------------
b (0)------------
c (1)------------
/ (1)----------------
e (0)--------------
f (1)--------------
前缀表达式:-+a*bc/ef
表达式二叉树
- (r)------------------
+ (0)----------------
a (0)--------------
* (1)--------------
b (0)------------
c (1)------------
/ (1)----------------
e (0)--------------
f (1)--------------
表达式二叉树
- (r)------------------
+ (0)----------------
a (0)--------------
* (1)--------------
b (0)------------
c (1)------------
/ (1)----------------
e (0)--------------
f (1)--------------
释放成功
--------------------------------
Process exited after 30.01 seconds with return value 0
请按任意键继续. . .
Process exited after 30.01 seconds with return value 0
请按任意键继续. . .