栈ADT应用:中缀表达式求值

表达式求值是进行数据处理的最基本操作。请编写程序完成一个简单算术表达式的求值。要求如下:
(1) 运算符包括:+-*-^(乘方)、括号
(2)运算量为数值常量,根据自己的能力可以对运算量做不同的约束,例如1位整数、多位整数、实数等(会有不同的测试用例);
输入:一行,即表达式,以“=”结束。例如:

5*(8-3)+6/5=

输出:一行,即表达式的值。结果值为整数时输出为整数,如果有小数时保留5位小数。

26.20000

输入

5+(3-1)/2=

输出

6

中缀表达式求值基于以下算法:
1.使用两个栈,num用于存储操作数,op用于存储操作符。
2.从左往右扫描,遇到操作数入栈num。
3.遇到操作符时,如果优先级低于或等于栈顶操作符优先级,则从num弹出两个元素进行计算,并压入num,继续与栈顶操作符的比较优先级。
4.如果遇到操作符高于栈顶操作符优先级,则直接入栈op。
5.遇到左括号,直接入栈op,遇到右括号,则直接出栈并计算,直到遇到左括号。
据我推测,本题的测试用例包含一位整数与一位整数、一位整数与多位整数、多位整数与多位整数、一位整数与浮点数、多位整数与浮点数、浮点数与浮点数6种情况。
代码中的一些小细节是在一次次测试中发现的。这个题又折腾了好久。。。

#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
template<class T>//模板类
class Stack
{
private:
    T a[2005];
    int up;
    int down;
public:
    Stack()
    {
        up=0;
        down=0;
    }
    void Push(T x)
    {
        a[up++]=x;
    }
    void Pop()
    {
        up--;
    }
    T Top()
    {
        return a[up-1];
    }
    int Size()
    {
        return up-down;
    }
    bool Empty()
    {
        return up==down?true:false;
    }
};
Stack<double>num;//double型和char型
Stack<char>op;
char equ[1005];
int prio[130]={0};
void getPriority()//设定优先级,加减<乘除<乘方,同级运算(加与减、乘与除之间)从左往右运算
{
    prio['+']=1;
    prio['-']=1;
    prio['*']=2;
    prio['/']=2;
    prio['^']=4;
}
int cmp(char theta1,char theta2)//优先级比较
{
    return prio[theta1]-prio[theta2];
}
double calculate(double n1,double n2,char op)
{
    if(op=='+')return n1+n2;//加
    else if(op=='-')return n1-n2;//减
    else if(op=='*')return n1*n2;//乘
    else if(op=='/')return n1/n2;//除
    else if(op=='^')return pow(n1,n2);//乘方
}
int main()
{
    int i;
    bool flag=false;//表示是否存在小数点
    double a,b,sum1=0,sum2=0,t=1,res;
    getPriority();
    scanf("%s",equ);
    int len=strlen(equ);
    for(i=0;i<=len-2;i++)
    {
        if(equ[i]>='0'&&equ[i]<='9')
        {
            if(flag==false)sum1=sum1*10+equ[i]-'0';//整数部分
            else
            {
                t=t/10;
                sum2+=double((equ[i]-'0'))*t;//小数部分
            }
        }
        else if(equ[i]=='.')flag=true;//表示存在小数
        else if(equ[i]=='+'||equ[i]=='-'||equ[i]=='*'||equ[i]=='/'||equ[i]=='^'||equ[i]=='('||equ[i]==')')
        {
            res=sum1+sum2;
            if(res!=0)//压栈是有条件的,不能把0压入,否则容易出现除0错误
            {
                num.Push(res);//遇到运算符表示操作数部分结束,将运算结果压栈
                res=0;
                sum1=0;
                sum2=0;
                t=1;
                flag=false;
            }
            if(equ[i]=='(')op.Push('(');//左括号直接压栈
            else if(equ[i]==')')//遇到右括号,操作数和运算符出栈并运算,直到遇到左括号为止
            {
                while(op.Top()!='(')
                {
                    a=num.Top();
                    num.Pop();
                    b=num.Top();
                    num.Pop();
                    num.Push(calculate(b,a,op.Top()));
                    op.Pop();
                }
                op.Pop();
            }
            else if(op.Empty()||op.Top()=='('||cmp(op.Top(),equ[i])<0)op.Push(equ[i]);//1.栈空 2.栈顶为左括号 3.栈顶运算符优先级小于当前运算符 3种情况下运算符压栈
            else if(cmp(op.Top(),equ[i])>=0)//栈顶运算符优先级不小于当前运算符(等于时表示同级运算)
            {
                while(cmp(op.Top(),equ[i])>=0)//栈顶运算符优先级不小于当前运算符(等于时表示同级运算,恰好能满足同级运算从左到右)
                {
                    a=num.Top();
                    num.Pop();
                    b=num.Top();
                    num.Pop();
                    num.Push(calculate(b,a,op.Top()));
                    op.Pop();
                    if(op.Empty())break;//运算符栈为空,说明操作数栈中只剩一个元素
                }
                op.Push(equ[i]);//当前运算符已经满足"栈顶运算符优先级小于当前运算符"这个条件,因此压栈
            }
        }
    }
    res=sum1+sum2;
    if(equ[len-2]!=')')num.Push(res);//尤其注意这里容易漏掉情况
    while(!op.Empty())//有些运算符可能留在栈中,因此最后再出栈并运算
    {
        a=num.Top();
        num.Pop();
        b=num.Top();
        num.Pop();
        num.Push(calculate(b,a,op.Top()));
        op.Pop();
    }
    if(num.Top()-int(num.Top())>1e-6)printf("%.5f",num.Top());//小数
    else printf("%d",int(num.Top()));//整数
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值