四则运算表达式树 C++模板 支持括号和未知数

首先允许我吐槽CSDN的MARKDOWN,简直难用的不行。

程序的原理是将表达式分治转换为二叉树,再在二叉树上递归计算结果。如同以下表达式: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。运行结果如下图所示:



转载于:https://www.cnblogs.com/HarryGuo2012/p/4524038.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值