【栈的应用】中缀,后缀表达式求值以及转换【简单计算器】

文章目录

  • 什么是中缀表达式
    • F. 简单计算器
    • 分析
      • 法一
      • 法一代码
      • 法二
      • 法二代码


在以前的文章中,我们讲了如何使用栈,今天我们要讲到栈的应用:

什么是中缀表达式

表达式就是一个算式,一般分为三种:前缀,中缀,后缀。中缀表达式就是我们在数学计算中所接触的表达式,而前缀或后缀就是把运算符的位置改变了,计算机更倾向于计算前缀或后缀表达式,因为他们不用考虑优先级。
我们经常看到这种题目

F. 简单计算器

内存限制:256 MiB
时间限制:1000 ms
标准输入输出
题目类型:传统
评测方式:文本比较
    
题目描述
读入一个只包含+、-、*、/、的非负整数计算表达式,计算该表达式的值。

输入格式
测试数据有多组,每组占一行。

每行不超过200个字符,整数和运算符之间用一个空格分隔。

没有非法表达式。

当一行中只有一个0时,表示输入结束,相应的结果不要输出。

输出格式
对于每组测试数据输出一行,即该表达式的值,精确到小数点后两位。

样例
输入样例
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
输出样例
12178.21

分析

法一

这个题目的难点在于需要让计算机考虑到优先级,那么我们可以用栈实现,首先用getline读入整行,然后依次判断当前元素是否为数字,若为数字,我们需要把它放进数字栈中,若为字符,我们需要把它放进运算符栈中
(此题较为简单,没有括号,若题目中有括号,则应将括号的优先级定为最大,我们在题目中也设有优先级表,若题目中含阶乘、乘方等运算,建议大家开一个二维数组来存储,本题较为基础,只有四种运算符,可以直接开一个长度为四的bool数组来存储即可:即加减法为0,乘除法为1,我们在比较时如果遇到相同优先级运算符,则可以直接计算栈顶运算,因为他排在前面,在优先级相同的情况下应优先计算,所以只要准备入栈的运算符比栈顶运算符小于等于即可直接计算)
我们只需要按照括号里的比较方法一一比较一一计算,将以计算过的运算符弹出,新运算符入栈,再拿两个数字栈中的元素进行当前的运算,最终把运算结果弹回栈中即可
(注意:一定是数字栈中第二个元素在前运算,因为他先入栈,就应该先执行运算,若栈顶到栈底元素依次为1,2,5,而现在要执行减法运算,则需执行2-1,而不是1-2,此点一定要注意。

法一代码

#include 
using namespace std;
char sp1, sp2;
double ans, num, tmp;
stack<double> s;
int main() {
	while (~scanf("%lf", &num)) {
		s.push(num);
		sp1 = getchar();
		if (num == 0 && sp1 != ' ') {
			break;
		}
	while (~scanf("%c%lf%c", &sp1, &num, &sp2)) {
		switch (sp1){
			case '+': s.push(num);break;
			case '-': s.push(-num);break;
			case '*': tmp= s.top();s.pop();
					  tmp*=num;s.push(tmp);break;
			case '/': tmp= s.top();s.pop();
					  tmp/= num;s.push(tmp);break;
		}
		if (sp2 != ' ') {
			break;
		}
	}
	ans = 0;
	while (!s.empty()) {
		ans += s.top();
		s.pop();
	}
	printf("%.2lf\n", ans);
	}
}

接下来还是这道题目,我们要讲第二种方法——转换为后缀表达式

法二

中缀转后缀的方法有很多种,这里只介绍其中一种(会更新)
依然是用到了一个运算符栈,每次遇到运算符就入栈,如果优先级比栈顶元素小,则栈顶元素出栈,(具体方法与前者相似,可参考)记录到一个后缀表达式的字符串中,然后继续比较,知道不满足条件则该字符入栈;如果遇到数字,则直接进入字符串中,如果遇到括号(本题无)则直接丢下不管。
最后我们会得到一个后缀表达式,计算后缀表达式就简单的多了:
遇到数字存入数字栈,遇到字符直接从数字栈中抽取两个数进行计算(注意顺序,与法一相同)
计算后再把结果丢入栈中,依次进行,直到最后只留下了一个数字,且原字符串以空,即计算完成,所剩的那个数字就是答案,下面给代码:

法二代码

/*此代码依然不保证AC,文中注释掉的代码,为调试所用,可忽略*/
//先转后缀 
#include
using namespace std;
int m[5]={2,0,0,1,1};
//这是运算符的优先级表;
/*先在运算符栈中加入\0,优先级最大为2。
加法与减法优先级为0,乘法与除法优先级为1 。
*/ 
char o;//临时储存变量,主要用来三变量交换 
int i,j,p,tem,one,two;//tem也是临时整形变量,减少重复计算次数 
stack<char>syms;//第一次存储符号 
stack<int>num;//第二次存储数字 
string s,suf;//s第一次读的中缀表达式,suf第二次的后缀表达式 
int Priority(char a){//用来查询优先表 
	if(a=='+') p=1;
	if(a=='-') p=2;
	if(a=='*') p=3;
	if(a=='/') p=4;
	if(a=='\0') p=0;
	//cout<<"zdbhf;onjl";
	return m[p];//直接返回值 
}
int calculate(char a,int one,int two){//用来计算后缀表达式 
	cout<<"GSDHIU";
	if(a=='+') 
		return two+one;
	if(a=='-') 
		return two-one;
	if(a=='*') 
		return two*one;
	if(a=='/') 
		return two/one;
}
int main(){
	syms.push('\0');//首先存入‘\0' 
	getline(cin,s);
	for(i=0;i<=s.size()-1;i++){
		if(s[i]==' ')
			continue;
		else if(s[i]>=48&&s[i]<=57){
			//cout<
			suf[j++]=s[i];
			cout<<j<<" "<<suf[j-1]<<endl;
		}
		else if(s[i]=='+'||s[i]=='-'||s[i]=='*'||s[i]=='/'){
			tem=Priority(s[i]);
			do{
				if(tem>=Priority(syms.top())){
					o=syms.top();
					suf[j++]=o; 
					//cout<
					syms.pop();
				}
			}while(tem>=Priority(syms.top()));
			syms.push(s[i]); 
		}
	}
	//cout<
	//cout<<"xu6re5tcsde55tyj";
	/*for(i=1;i<=suf.size();i++){
		cout<
	for(i=0;i<=suf.size()-1;i++){
		cout<<"gsji"<<endl;
		if(suf[i]>=48&&suf[i]<=57)
			num.push(suf[i]);
		else {
			one=num.top();
			num.pop();
			two=num.top();
			num.pop();
			num.push(calculate(suf[i],one,two));
		}
	}
	cout<<"hxgfd"<<num.top();
}

今天就讲到这里啦,下次再见。

你可能感兴趣的:(表达式,算法,c++)