四则运算表达式树 C++模板 支持括号和未知数
2015-02-14 22:28
537 查看
首先允许我吐槽CSDN的MARKDOWN,简直难用的不行。
程序的原理是将表达式分治转换为二叉树,再在二叉树上递归计算结果。如同以下表达式:x+5*y-(6/(1-5.5))可以表达为以下二叉树(抱歉,本来想弄Visualgo的,结果上不了,只能用word来做画面了):
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/02/666368faddb82e994f515b6ea710a716)
为什么是这样的二叉树呢?仔细想想平时是怎么计算这个表达式的,毫无疑问的是最后一步是减法,倒数的第二步是加法和除法……所以我们不难得出,将最后一步作为整个树的根,然后将这次运算的左边的表达式(可能是运算、未知数、常数)和右边的表达式进行递归建树,就能建立这样的二叉树了。
那为什么要建立这个二叉树呢?如果这样的二叉树建立好了,就很容易递归(从下至上)得到答案了:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/02/3c870b5191cf5e334c63d16e26e67e8d)
那么现在的问题就在于如何将表达式字符串转换为表达式树,从先前的说明中,我们不难发现,关键就在于寻找当前表达式中的最后一步,作为子树的根。为了达到这个目的,我们首先去除掉包含整个表达式的括号,这样一来,最后一步计算一定不在括号中,这里可以用一个变量brackets来标记,初始化brackets=0,每当遇到左括号就++,右括号就--,所以,当且仅当brackets==0时,当前的位置是处在括号外的。在扫的过程中,不断的更新在括号外的最后加减法和最后乘除法的位置。最后得到的加减法的位置(如果没有加减法,就用乘除)就是当前表达式的最后运算。
未知数的处理是靠map实现的。
整个程序由基类表达式类和三个继承自表达式类的类组成,其中Evaluate函数(返回计算数据)由Expression类定义,由三个派生类重写。函数strToTree是将字符串转换为表达式二叉树:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/02/fda51c6f23e7807ebb6f8d3431f1c968)
详细见代码,注释写的非常详细:
这里我设置了两个未知数x,y。运行结果如下图所示:
程序的原理是将表达式分治转换为二叉树,再在二叉树上递归计算结果。如同以下表达式:x+5*y-(6/(1-5.5))可以表达为以下二叉树(抱歉,本来想弄Visualgo的,结果上不了,只能用word来做画面了):
为什么是这样的二叉树呢?仔细想想平时是怎么计算这个表达式的,毫无疑问的是最后一步是减法,倒数的第二步是加法和除法……所以我们不难得出,将最后一步作为整个树的根,然后将这次运算的左边的表达式(可能是运算、未知数、常数)和右边的表达式进行递归建树,就能建立这样的二叉树了。
那为什么要建立这个二叉树呢?如果这样的二叉树建立好了,就很容易递归(从下至上)得到答案了:
那么现在的问题就在于如何将表达式字符串转换为表达式树,从先前的说明中,我们不难发现,关键就在于寻找当前表达式中的最后一步,作为子树的根。为了达到这个目的,我们首先去除掉包含整个表达式的括号,这样一来,最后一步计算一定不在括号中,这里可以用一个变量brackets来标记,初始化brackets=0,每当遇到左括号就++,右括号就--,所以,当且仅当brackets==0时,当前的位置是处在括号外的。在扫的过程中,不断的更新在括号外的最后加减法和最后乘除法的位置。最后得到的加减法的位置(如果没有加减法,就用乘除)就是当前表达式的最后运算。
未知数的处理是靠map实现的。
整个程序由基类表达式类和三个继承自表达式类的类组成,其中Evaluate函数(返回计算数据)由Expression类定义,由三个派生类重写。函数strToTree是将字符串转换为表达式二叉树:
详细见代码,注释写的非常详细:
#include<iostream> #include<vector> #include<cstdio> #include<map> #include<cstring> #include<string> using namespace std; typedef map<string, double> MSD; //表达式类 class Expression { public: virtual double Evaluate(MSD vars){ return 0; } }; //常数类 class Constant :public Expression { public: double value; Constant(double value) { this->value = value; } double Evaluate(MSD vars) { return value; } }; //未知数类 class VariableReference :public Expression { public: string name; VariableReference(string name) { this->name = name; } double Evaluate(MSD vars) { double value = vars[name]; return value; } }; //运算类 class Operation :public Expression { public: //左边的表达式 Expression *left; //当前运算 char op; //右边的表达式 Expression *right; Operation(Expression *left, char op, Expression *right) { this->left = left; this->op = op; this->right = right; } //计算值 double Evaluate(MSD vars) { //递归计算 double x = left->Evaluate(vars); double y = right->Evaluate(vars); //运算 switch (op) { case '+':return x + y; case '-':return x - y; case '*':return x*y; case '/':return x / y; } } }; //将字符串转换为树 //s起始位置,t结束位置 Expression *strToTree(string str, int s, int t) { //去除包含整个当前表达式的括号 while (s <= t&&str[s] == '('&&str[t] == ')')s++, t--; if (s > t) return new Constant(0); //findLetter找到字母,用以判断是否为未知数 //findChar找到字符,用以判断是否存在运算符 bool findLetter = false, findChar = false; //括号标记 int brackets = 0; //lastPS最后的加减法 //lastMD最后的乘除 int lastPS = -1, lastMD = -1; for (int i = s; i <= t; i++) { //当前位置不是常数 if (str[i] != '.' && (str[i]<'0' || str[i]>'9')) { //如果是字母的话 if ((str[i] >= 'a'&&str[i] <= 'z') || (str[i] >= 'A'&&str[i] <= 'Z')) findLetter = true; else { //不是常数,不是字母,就是运算符 findChar = true; switch (str[i]) { case '(':brackets++; break; case ')':brackets--; break; //更新最后加减法的位置 case '+': case '-':if (!brackets)lastPS = i; break; //更新最后乘除法的位置 case '*': case '/':if (!brackets)lastMD = i; break; } } } } //从s到t都是常数 if (findLetter == false && findChar == false) return new Constant(stod(str.substr(s, t - s + 1))); //从s到t是未知数 if (findChar == false) return new VariableReference(str.substr(s, t - s + 1)); //从s到t是个运算 //没有加减就用乘除 if (lastPS == -1) lastPS = lastMD; return new Operation(strToTree(str, s, lastPS - 1), str[lastPS], strToTree(str, lastPS + 1, t)); } int main() { MSD vars; //这里设置未知数 vars["x"] = 123; vars["y"] = 100; //输入字符串 string str; cin >> str; //转换为树 Expression *exp = strToTree(str, 0, str.length() - 1); //输出值 cout << exp->Evaluate(vars) << endl; return 0; }
这里我设置了两个未知数x,y。运行结果如下图所示:
相关文章推荐
- 四则运算表达式树 C++模板 支持括号和未知数
- C++实现支持浮点(x.x)的四则运算(带括号)
- C++ 单位整型计算器,仅支持带括号的四则运算。初学c++第一个个人作品
- 支持多位数,括号,四则运算,的计算器算法c++实现
- 命令模式-归约-计算器-支持四则运算、多重括号
- c++ 直接中缀表达式求值 仅支持正整数的四则混合运算
- c++通过栈和递归函数实现简单四则运算以及括号的的计算器
- 支持括号、分数计算的四则运算计算器
- 四则运算问题扩充:1、题目避免重复;2、可定制(数量/打印方式);3、可以控制下列参数: 是否有乘除法、是否有括号、 数值范围、加减有无负数、除法有无余数、否支持分数 (真分数, 假分数, …)、是否支持小数 (精确到多少位)、打印中每行的间隔可调整;
- C语言:(新)四则计算器(支持括号和次方运算)
- 计算表达式的值(仅含有四则运算和支持括号嵌套,浮点数运算)
- Java实现四则运算计算器(支持括号,小数,负数)
- C/C++带括号的四则运算
- 四则运算表达式解析和求值(支持括号、小数)
- 【汇编】计算器,支持带括号的四则混合运算
- C++ 四则运算表达式(简单的+-*/带括号) 栈
- 不带括号的四则运算步骤
- 四则混合运算C++代码(中缀表达式)
- 带括号的四则运算表达式的求值(栈实现)
- 计算中缀表达式(实数,四则运算与括号)(双栈)