stack应用——简单计算器

题目链接

自己的解题思路(略显麻烦)

        准备两个堆栈分别存放运算符(op)和待操作的数字(number)。先将表达式以string类型保存到str变量里,再以空格为间隔符,把多个str的子串存在string数组arr中,遍历数组arr。

        若元素是一个数字:op的头部是*或者/运算符的话,就执行该运算操作;反之,则把该元素添加到number堆栈中

        若元素是一个运算符:若op不为空且该元素的level为1,就先执行op头部的运算操作,再将该元素添加到op堆栈中。反之则直接添加。

总结一下,就是自己也觉得代码越写越乱。不断查测试用例,然后不断改bug,这样的做题方式是不对的。最关键的是,我一开始没认识到,如果多个同level的运算符相连,是按从左到右的方式运算;而如果是根据堆栈弹出的顺序,则等价于从右到左的顺序,显然这不能产生正确的题解。

#include <iostream>
#include <stack>
#include <cstdio>
using namespace std;
float Tran(string x) {
    float ans=0;
    for(int i=0; i<x.size(); i++) {
        ans*=10;
        ans+=(x[i]-'0');
    }
    return ans;
}
bool isNum(string x) {
    if(x[0]>='0' && x[0]<='9')
        return true;
    return false;
}
int getLevel(string x) {
    if(x=="+" || x=="-")
        return 1;
    return 2;
}
float funCal(float a, float b,  string op) {
    if(op=="+")
        return a+b;
    else if(op=="-")
        return a-b;
    else if(op=="*")
        return a*b;
    else
        return a/b;
}
int main() {
    string str;
    while(getline(cin,str) && str!="0") {
        stack<float> number;
        stack<string> op;
        string arr[201];
        int pre=0,len=str.size(),arrlen=0;
        for(int i=0; i<len; i++) {
            if(str[i]==' ') {
                arr[arrlen++]=str.substr(pre, i-pre);
                pre=i+1;
            }
        }
        arr[arrlen++]=str.substr(pre);
        for(int i=0; i<arrlen; i++) {
            if(isNum(arr[i])) {
                if(op.size()>0 && (op.top()=="*" || op.top()=="/")) {
                    float a = number.top();
                    number.pop();
                    number.push(funCal(a,Tran(arr[i]),op.top()));
                    op.pop();
                } else
                    number.push(Tran(arr[i]));
            } else {
                if(op.size()>0 && getLevel(arr[i])==1) {
                    float b=number.top();
                    number.pop();
                    float a=number.top();
                    number.pop();
                    number.push(funCal(a,b,op.top()));
                    op.pop();
                }
                op.push(arr[i]);
            }
        }
        while(number.size()>1) {
            float b=number.top();
            number.pop();
            float a=number.top();
            number.pop();
            number.push(funCal(a,b,op.top()));
            op.pop();
        }
        printf("%.2f\n",number.top());
    }
    return 0;
}

比较清晰的思路

  1. 设立运算符和运算数两个栈,一个用来存储运算符,另一个用来存储运算数。
  2. 在运算符栈中放置一个特殊运算符#其优先级最低。
  3. 将表达式尾部添加一个特殊运算符$,其优先级次低。
  4. 从左到右依次遍历字符串,若遍历到运算符,则将其与运算符栈的栈顶元素进行比较,若运算符栈的栈顶的优先级小于该运算符,则将该运算符压入运算符栈;若运算符栈的栈顶的优先级大于等于该运算符,则弹出该栈顶运算符,从运算数栈中依次弹出运算数,完成弹出运算符对应的运算后,再将该结果压入运算数栈。
  5. 若遍历到表达式中的运算数,则直接压入运算数栈。
  6. 若运算符栈中仅剩两个特殊运算符#和$,则表达式运算结束,此时运算数栈中唯一的数字就是表达式的值。

将运算符栈栈底的特殊运算符#的优先级设为最低,于是后续遇到的任何运算符的优先级都会比它高,能够顺利入栈。

将表达式尾部的特殊运算符$的优先级设为次低,于是当遍历到表达式尾部时,若运算符栈中尚有可以运算的符号,则可继续运算;若运算符栈中只剩下特殊符号#,则也可保证$能够顺利入栈,并且可以让程序正确地退出。

isdigit()函数在头文件中cctype中,该函数能够用来检查字符是否为十进制数字字符。

#include <iostream>
#include <cstdio>
#include <cctype>
#include <string>
#include <stack>
using namespace std;
int Priority(char c){
    if(c=='#') return 0;
    else if (c=='$') return 1;
    else if(c=='+'||c=='-') return 2;
    else return 3;
}
double GetNumber(string str,int& index){
    double number = 0;
    while(isdigit(str[index])){
        number = number*10+str[index++]-'0';
    }
    return number;
}
double Calculate(double x,double y,char op){
    if(op=='+') return x+y;
    else if(op=='-') return x-y;
    else if(op=='*') return x*y;
    else return x/y;
}
int main(){
    string str;
    while(getline(cin,str) && str!="0"){
        int index=0;  // 字符串下标
        stack<char> oper;  // 运算符栈
        stack<double> data;  // 运算数栈
        str+='$';  // 字符串尾部添加$
        oper.push('#');  // 运算符栈底添加#
        while(index<str.size()){
            if(str[index]== ' ') index++;
            else if(isdigit(str[index])) data.push(GetNumber(str, index));
            else{
                if(Priority(oper.top())<Priority(str[index])){
                    oper.push(str[index++]);
                }else{
                    double y=data.top();
                    data.pop();
                    double x=data.top();
                    data.pop();
                    data.push(Calculate(x,y,oper.top()));
                    oper.pop();
                }
            }
        }
        printf("%.2lf\n",data.top());
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值