codeup1918 简单计算器

题目描述

读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。

输入

测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。

输出

对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。

样例输入

30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0

样例输出

12178.21

 此题考查的是栈和队列的运用,可以使用两个栈和一个队列来解决,也可以只使用两个栈。

解题思路共两步:

(1)将中缀表达式转为后缀表达式

        这一步需要一个队列来装后缀表达式和一个栈来装符号。先从左到右遍历中缀表达式的每个符号和数字,若是数字就入队;若是符号,则判断其与栈顶符号的优先级,若该符号(是右括号或其)优先级低于或等于栈顶符号,则栈顶元素依次出栈并进入队列,直到当前符号优先级大于栈顶符号,就将当前符号进栈。一直到最终输出后缀表达式。

(2)对后缀表达式求值

        这一步只需一个栈来装数字。

        1.先初始化一个空栈,开始遍历后缀表达式。

        2.如果字符是一个操作数,则令其入栈。

        3.如果字符是个运算符,则弹出栈里的两个操作数(一定会有两个数在栈里,因为是后缀表达式),进行运算,再把结果入栈。

        4.到后缀表达式末尾,从栈中弹出结果。

理解上面两步后,可以将两步合并,最终只使用2个栈就可一次性求出答案:

        1.初始化两个栈,分别是数字栈和符号栈。

        2.遍历中缀表达式,如果是数字,则入数字栈。

        3.如果是符号,则判断其与符号栈的栈顶符号的优先级。若当前符号(是右括号或其)优先级低于或等于栈顶符号,则栈顶元素出符号栈,并在数字栈弹出两个数进行相应运算,再使结果入数字栈。重复该步骤直至当前符号优先级大于栈顶符号,则将当前符号入符号栈。

        4.当读完中缀表达式后,则将符号栈里的符号依次出栈,从数字栈弹出两个数进行相应运算,再把结果入数字栈,直到最后一个符号出栈。把数字栈的数字弹出,即为结果。

 

符号优先级可以使用map来设置,使每个符号对应一个数值,数值越高说明优先级越高。加减号的优先级相等,乘除号的优先级相等,而加减号优先级低于乘除号。

下面是具体的代码实现:

#include
#include
#include
#include
#include
using namespace std;

/**
易错点:
1、stack的pop()返回值为空,因此要获取栈顶元素并出栈,只能先top()后pop()。
2、当前符号优先级高于栈顶符号时,入栈;
   当前符号优先级低于或等于栈顶符号时,弹出栈顶符号计算。
   (注意等于的情况是弹出!)
3、for循环最后没写i++时,在代码中尤其注意i++的位置,很容易忘记写i++或写错位置。
4、使用top()就一定要同时判空作为条件。
5、读入一行str,要用getline(cin,str);逗号表达式的值为最后一个逗号后的值。
*/

string str;
map op; //设置符号优先级的map
stack num; //数字栈
stack sym; //符号栈

double calculator();
double calculate();

int main(){
    op['+']=1; //给符号设置优先级
    op['-']=1;
    op['*']=2;
    op['/']=2;
    while(getline(cin,str),str!="0"){
        for(string::iterator it = str.begin();it!=str.end();it++){
            if(*it==' '){
                str.erase(it);  //把表达式中的空格去掉
            }
        }
        //开始计算当前表达式的值
        double result = calculator();
        printf("%.2f\n",result);
    }

}


double calculator(){
    for(int i=0;i='0' && str[i]<='9'){  //若当前符号是数字
            int temp = str[i++]-'0';
            while(i='0'&&str[i]<='9'){
                temp = temp*10+(str[i]-'0'); //获取操作数
                i++;
            }
            num.push(temp); //操作数入栈

        }else{  //当前符号是操作符
            if(sym.empty() || op[str[i]]>op[sym.top()]){ //符号栈为空或当前符号优先级大于栈顶符号,则当前符号直接入栈
                sym.push(str[i]);

            }else{ //当前符号优先级小于等于栈顶符号,则一直将栈顶符号出栈计算,直至当前符号优先级大于栈顶符号
                while(sym.size()!=0 && op[str[i]] <= op[sym.top()]){
                     //弹出栈顶符号和两个数字并计算
                    calculate();
                }
                sym.push(str[i]); //当前符号入符号栈
            }
            i++;
        }
    }
    //表达式读完了,所有数字、符号都入栈了,开始计算剩下的
    while(sym.size()){ //符号栈还有符号
        //弹出符号和两个数字并计算
        calculate();
    }

    return num.top(); //数字栈顶元素就是最终结果

}

double calculate(){
    char top_op = sym.top(); //栈顶符号出栈
    sym.pop();
    double num1 = num.top(); //数字栈弹出两个数字
    num.pop();
    double num2 = num.top();
    num.pop();
    double result;
    switch(top_op){
        case '+':
            result = num1+num2;
            break;
        case '-':
            result = num2-num1;
            break;
        case '*':
            result = num1*num2;
            break;
        case '/':
            result = num2/num1;
            break;
    }
    num.push(result); //计算结果入数字栈

}

中途遇到过“答案错误50”的情况,原因是每一组输出没换行(代码中已加上)。

最后是我敲代码过程中的一些易错点:

1、stack的pop()返回值为空,因此要获取栈顶元素并出栈,只能先top()后pop()。
2、当前符号优先级高于栈顶符号时,入栈;
      当前符号优先级低于或等于栈顶符号时,弹出栈顶符号计算。
   (注意等于的情况是弹出!)
3、for循环最后没写i++时,在代码中尤其注意i++的位置,很容易忘记写i++或写错位置。
4、使用top()就一定要同时判空作为条件。
5、读入一行str,要用getline(cin,str);逗号表达式的值为最后一个逗号后的值。

你可能感兴趣的:(算法学习)