算法笔记codeup 1918 简易计算器详解(用c++栈实现计算器)

codeup 1918 简易计算器

题目描述

读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
输入
测试输入包含若干测试用例,每个测试用例占一行,每行不超过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.1 将中缀表达式转换为后缀表达式:**设立一个操作符栈,用于存放操作符,一个操作数队列,用于存放生成的后缀表达式

1.2 这里数字优先级最高其次式 ‘*’ , ‘/’,再其次是加减,括号的优先级最低(这个优先级和平时我们说的先算小括号里面的不是一个概念,这里实际上还是先算小括号里面的),从左到右扫描中缀表达式,如果是数字,就放到队列中(优先级最高),如果是操作符,就要保证这个操作符放到栈中的时候,它的优先级大于原栈顶元素的优先级,也就是说,假如是乘号,原栈顶元素是加号,直接放入栈中就行了;如果是加号,而原栈顶元素是乘号,就要弹出原栈顶,放到队列中,就这样一直弹,弹到栈顶元素比我们要放的元素优先级低就好了。当我们把中缀表达式扫描完后,如果栈中还有操作符,我们就依次弹到队列里。栈空时,一个完整的后缀表达式就形成了。

1.3 如果有括号了也很好处理,遇到左括号,直接放到栈中就行,由于左括号默认优先级最低,所以你之后再处理其他操作符,它仍会正常处理,遇到右括号后,不用放到栈中,直接开始弹栈顶元素,直到遇到左括号(左括号不放到队列中,相当于一个标记)

2计算后缀表达式

遇到数字,放到栈中(与个数无关,照着后缀表达式放就完事了),遇到操作符,就弹出来两个数进行计算,(这里要注意:弹出的第一个数,其实是第二个操作数,弹出的第2个数,其实是第一个操作数,比如:符号是/号,弹出第一个数是2,第二个人弹得是1,实际上计算式是 1/2),计算结果放入栈中,继续扫描后缀表达式,直到最终栈中只剩了一个数,结果就出来了

ps:写这种题得耐心,还得勤动手,可以自己画画图,模拟实现一下过程,这样会更清楚


#include <iostream>
#include <stdio.h>
#include <stack>
#include <queue>
#include <map>
using  namespace std;
struct node{
    double num;
    char op;
    bool flag;//true表示操作数,false表示操作符
};
string str;
stack< node > s;
queue< node > q;
map< char , int> op;
void change(string str)
{
    for (int i = 0; i <str.length() ;) {
        if(str[i]>='0' && str[i] <='9')//如果是数字
        {
            struct node temp;
            temp.flag = true;
            temp.num = str[i] - '0';
            i++;
            while (i <str.length()&&str[i]>='0' && str[i] <='9' )
            {
                temp.num = temp.num * 10 + (str[i] - '0');
                i++;
            }
            q.push(temp);
        }
        else if(str[i]=='(' || str[i]==')')
        {
            if(str[i] == '(')
            {
                struct node x;
                x.op = str[i];
                x.flag = false;
                s.push(x);
            }else
            {
                while (s.top().op!='(')
                {
                    q.push(s.top());
                    s.pop();
                }
                s.pop();//在栈中去除左括号
            }
            i++;
        }else
        {
            struct node temp;
            temp.flag = false;
            temp.op = str[i];
            while(!s.empty()&&op[temp.op] <=op[s.top().op])
            {
                q.push(s.top());
                s.pop();
            }
            s.push(temp);
            i++;
        }
    }
    //在遍历完中缀表达式后,把栈中的操作符一次弹出到后缀表达式中
    while(!s.empty())
    {
        q.push(s.top());
        s.pop();
    }
}
double cal()
{
    node cur;
    node temp;
    double temp1,temp2;
    while(!q.empty())
    {
        cur = q.front();
        q.pop();
        if(cur.flag == true)
        {
            s.push(cur);
        }else
        {
            temp2 = s.top().num ;
            s.pop();
            temp1 = s.top().num;
            s.pop();
            temp.flag = true;
            if(cur.op =='+')
            {
                temp.num = temp1 + temp2;
            }else if(cur.op =='-')
            {
                temp.num = temp1 - temp2;
            }else if(cur.op =='*')
            {
                temp.num = temp1 * temp2;
            }else if(cur.op =='/')
            {
                temp.num = temp1 / temp2;
            }
            s.push(temp);
        }
    }
    return s.top().num;
}
int main(void)
{
//    这里设置为1和2其实是有门道的,因为‘(’默认为0了,优先级最低
    op['+'] = op['-'] = 1;
    op['*'] = op['/'] = 2;
    while(getline(cin,str),str!="0")//由于cin读取字符串时,遇到空格就会停止,所以用了getline,可以读一行
    {
        for(string::iterator it =str.end(); it!=str.begin();it--)
        {
            if(*it == ' ')
            {
                str.erase(it);//把表达式中的空格全部去掉
            }
        }
        while(!s.empty())
        {
            s.pop();//初始化
        }
        change(str);//转为后缀表达式
        printf("%.2f",cal());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值