计算机软件技术实习Week1:算术表达式计算方法的学习与双栈算符优先级法的代码实现

目录

1. 算术表达式的计算方法

方法一:将中缀表达式转化为后缀表达式,再计算后缀表达式得出结果

方法二:使用双栈算符优先级法进行计算

方法三:用二叉树来求解后缀表达式的值

2. 双栈算符优先级法的代码实现


1. 算术表达式的计算方法

人类在进行四则运算时,会选用最直观的中缀表达式进行计算。这种表达式的特点是运算符在两个运算数字的中间,符合人类的思维习惯。

但是,由于计算机严格从左向右读入数据的特性,导致其无法很好地处理加减乘除的优先级、优先计算括号等问题,因此使用计算机求解算式时并不能使用中缀表达式。

对于该问题,有几种解决方法:

方法一:将中缀表达式转化为后缀表达式,再计算后缀表达式得出结果

该方法的原理为使用一个栈暂时储存运算符,同时将数值与处理后的运算符放到队列中。

① 遇到数值,将其放入队列;

② 遇到 '(' ,将其放入栈;

③ 遇到运算符,则比较该运算符与栈顶运算符的优先级:若栈顶运算符比该运算符优先级低,则入栈;若栈顶运算符比该运算符优先级高或与该运算符优先级相同,则不断弹出栈中运算符进入队列,直到栈顶运算符比该运算符优先级低或栈为空,满足条件后将该运算符入栈。

④ 若遇到 ')' ,则依次弹出栈中的运算符进入队列,直到遇到 '(' ;

⑤ 当运算表达式读入结束时,依次弹出栈中的所有运算符进入队列,直到栈为空。

如:中缀表达式 A + ( B - C / D ) * E 相对应的后缀表达式为 A B C D / - E * +

以上步骤完成了表达式的转化,接下来进行后缀表达式的计算:

新建一个栈来进行值的计算。 从左向右扫描后缀表达式,若遇到操作数,则压入该栈;若遇到运算符,则将栈顶的两个操作数弹出栈进行计算,再将结果压入栈。不断进行此过程直到结束,将栈中唯一的元素弹出栈。

表达式 A B C D / - E * + 的计算流程如下图所示。

方法二:使用双栈算符优先级法进行计算

该方法需要建立两个栈,分别为运算数栈与运算符栈。与方法一不同的是,该方法并没有严格的“转化表达式”与“对转化后的表达式进行运算”之分,更像是同时进行。

该方法的具体流程为:

① 遇到运算数,直接将其放入运算数栈;

② 遇到 '(' ,直接将其放入运算符栈;

③ 遇到运算符,则比较其与栈顶运算符的优先级:若该运算符比栈顶运算符优先级高,则直接将其放入运算符栈;若该运算符比栈顶运算符优先级低或与栈顶运算符优先级相同,则运算数栈弹出两个运算数、运算符栈弹出一个运算符,进行一次运算,将运算结果放入运算数栈,该过程结束后将该运算符放入运算符栈;

④ 遇到 ')' ,则不断弹出两个运算数与一个运算符进行运算,结果放入运算数栈中,直到遇到 '(' ;

⑤ 当运算表达式读入结束时,则不断弹出两个运算数与一个运算符进行运算,结果放入运算数栈中,直到运算符栈为空。

方法三:用二叉树来求解后缀表达式的值

使用该方法计算表达式 A B C D / - E * + 的流程如下图所示。

 该方法需要用到较为复杂的二叉树结构,且编程实现工程量较大,故不多赘述。

2. 双栈算符优先级法的代码实现

以上三种方法各有特点与优劣。但相较于其他两种方法,双栈算符优先级法流程更加清晰、数据分类更加明确,因此选择双栈算符优先级法进行代码实现。

以下为双栈算符优先级法求解算术表达式的实现代码:

#include <bits/stdc++.h>

using namespace std;

stack<double> num;
stack<char> op;

bool is_number(string s, int i) 
{
	if((s[i]>='0' && s[i]<='9') || s[i]=='.') return 1;
	return 0;
}

bool is_new(string s, int i)
{
	if((!is_number(s, i-1)) || i==0) return 1;
	return 0;
}

double calc(double x, double y, char op)
{
	switch(op)
	{
		case '+' : return(x+y);
		case '-' : return(x-y);
		case '*' : return(x*y);
		case '/' : return(x/y);
	}
}

bool is_prior(char newop, char op)
{
	if((newop=='*' || newop=='/') && (op=='+' || op=='-')) return 1;
	if(op=='@' || op=='(') return 1;
	return 0;
}

int main()
{
	string s;
	int flag = 0;
	double n;
	
	cin >> s;
	
	op.push('@');
	num.push(0);
	
	for(int i=0; i<s.length(); i++)
	{
		if(is_number(s, i))
		{
			if(is_new(s, i))
			{
				num.push(s[i]-'0');
			}
			
			else
			{
				if(s[i]=='.')
				{
					flag = 1;
					continue;
				}
				
				if(!flag)
				{
					num.top() = num.top()*10+s[i]-'0';
				}
				
				else
				{
					num.top() = num.top()+(s[i]-'0')/pow(10, flag);
					flag++;
				}
			}
		}
		
		else
		{
			flag = 0;
			
			if(s[i]=='(')
			{
				op.push(s[i]);
			}
			
			else if(s[i]==')')
			{
				while(op.top()!='(')
				{
					n = num.top();
					num.pop();
					num.top() = calc(num.top(), n, op.top());
					op.pop();
				}
				op.pop();
			}
			
			else if(s[i]=='=')
			{
				while(op.top()!='@')
				{
					n = num.top();
					num.pop();
					num.top() = calc(num.top(), n, op.top());
					op.pop();
				}
			}
			
			else
			{
				if(is_prior(s[i], op.top()))
				{
					op.push(s[i]);
				}
				
				else
				{
					while(!is_prior(s[i], op.top()))
					{
						n = num.top();
						num.pop();
						num.top() = calc(num.top(), n, op.top());
						op.pop();
					}
					op.push(s[i]);
				}
			}
		}
	}
	
	cout << num.top();
	
	return 0;
}

对于完全正确的输入,该代码可以给出正确的结果。效果如图所示:

但这份代码并不完整。首先,它无法进行含有负数的运算;其次,它不具备判断输入错误的能力,如括号不匹配、小数点使用错误、运算符连用等,需要进一步修改与完善。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值