在以前的文章中,我们讲了如何使用栈,今天我们要讲到栈的应用:
表达式就是一个算式,一般分为三种:前缀,中缀,后缀。中缀表达式就是我们在数学计算中所接触的表达式,而前缀或后缀就是把运算符的位置改变了,计算机更倾向于计算前缀或后缀表达式,因为他们不用考虑优先级。
我们经常看到这种题目
内存限制: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();
}
今天就讲到这里啦,下次再见。