栈的应用(表达式求值)

利用栈编写表达式求值程序:输入含有“+”、“-”、“*”、“/”四则运算的表达式,其中负数要用(0-正数)表示,并以=结束。要求输出表达式的值(两运算符号的优先关系见教材表3.1)。

输入格式
第一行:一个算术表达式

输出格式
第一行:算术表达式的值

输入样例
3*(9-7)=

输出样例
6
附运算符的优先关系
const char priority[][7] = //算符间的优先级关系
{
{ ‘>’,’>’,’<’,’<’,’<’,’>’,’>’ },
{ ‘>’,’>’,’<’,’<’,’<’,’>’,’>’ },
{ ‘>’,’>’,’>’,’>’,’<’,’>’,’>’ },
{ ‘>’,’>’,’>’,’>’,’<’,’>’,’>’ },
{ ‘<’,’<’,’<’,’<’,’<’,’=’,‘0’ },
{ ‘>’,’>’,’>’,’>’,‘0’,’>’,’>’ },
{ ‘<’,’<’,’<’,’<’,’<’,‘0’,’=’ },
};

#include <iostream>
#include <stack>//引用栈的类
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
using namespace std;
stack<char>opter;//操作符栈
stack<double>operand;//操作数栈

int GetIndex(char c)//查找操作符在表中的位置,以便比较
{
    int index=0;
    switch(c)
    {
    case'+':
        index=0;
        break;
    case'-':
        index=1;
        break;
    case'*':
        index=2;
        break;
    case'/':
        index=3;
        break;
    case'(':
        index=4;
        break;
    case')':
        index=5;
        break;
    case'=':
        index=6;
        break;
    default:break;
    }
    return index;//这个代码是有缺陷的,如果读入的是@¥% 等无关的字符的时候,这里返回的index就默认为+,可是OJ上并没有这样的测试数据。

}

char GetPriority(char theta1,char theta2)
{
    const char priority[][7] =     //算符间的优先级关系
	{
		{ '>','>','<','<','<','>','>' },
		{ '>','>','<','<','<','>','>' },
		{ '>','>','>','>','<','>','>' },
		{ '>','>','>','>','<','>','>' },
		{ '<','<','<','<','<','=','0' },
		{ '>','>','>','>','0','>','>' },
		{ '<','<','<','<','<','0','=' },
	};
	int index1=GetIndex(theta1);
	int index2=GetIndex(theta2);
	return priority[index1][index2];
	//将返回两个的优先级关系
}

double operate(double b,char c,double a)//注意模拟的时候要注意顺序
{
    switch(c)
    {
    case'+':
        return a+b;
    case'-':
        return b-a;
    case'*':
        return b*a;
    case'/':
        return b/a;
    default:
        break;
    }
}

double GetAns()
{
    //操作符栈是opter,操作数栈是operand
    opter.push('=');//这里说明一下为什么先压=入栈,=是一个本次操作是否结束的标志,开始的时候由于读入的不是=,是当前栈顶元素是=,所以不会引起结束
    int counter=0;//用于计算每一个数的单位
    char c=getchar();
    while(c!='='||opter.top()!='=')
    {
        if(isdigit(c))
        {
            if(counter==1)
            {
                double t=operand.top();
                operand.pop();
                operand.push(t*10+(c-'0'));
                counter=1;
            }
            else
            {
                operand.push(c-'0');
                counter++;
            }
            c=getchar();
        }
        else
        {
            counter=0;
            switch(GetPriority((opter.top()),c))
                   {
                   case '<':
                    opter.push(c);
                    c=getchar();
                    break;
                   case'=':
                    opter.pop();//优先表中只有()会出现等号,所以这个是用来消除等号的
                    c=getchar();
                    break;
                   case'>'://如果小于了,那么在这一步需要计算
                    char theta=opter.top();
                    opter.pop();
                    double a=operand.top();
                    operand.pop();
                    double b=operand.top();
                    operand.pop();
                    operand.push(operate(b,theta,a));
                   }
        }
    }
    return operand.top();
}

int main()
{
    double ans=GetAns();
    cout<<ans;
    return 0;
}

问题算法解析与代码分解解析
算法分析:
首先问题是要求我们用栈来解决多项式的问题,这里采用的一个比较巧妙的方法是运算符优先级的方法,首先我们知道栈是一类顺序表或者链表,那么存储的数据类型应该是一致的,除非我们用结构体来解决数据存储的问题,否则无法解决因为数据类型不同而带来的问题,那么我们需要两个工作栈,这两个工作栈,一个用来存储操作数(operand),一个用来存储操作符(opter),而且根据栈的规则,读取都是从第一个元素开始读,在后面的元素都会再栈顶中出现,那么这个优先级表就可以起到作用。
我们来举个例子3*(9-7)
注意:运算符的优先级是由栈顶元素来确定的,而不是当前的ch
我们来模拟入栈的过程,首先读取到3,发现是数字型字符,然后把3存入stack.operand中,接着继续读取
发现是*,是操作符,读入并存入操作符栈,进入第一次switch判断优先级,由于=的优先级是最低的,所以是入栈,进行下一次读取,接着继续读取
发现是(,是操作符,读入并存入操作符栈,接着继续读取.
发现是9,是操作数,入栈,接着继续读取
发现是操作符,注意,这时候已经出现了一对操作符,是时候应该要进行优先级的获取了,读取之后,先不入栈,先进行switch判断,然后发现上一个是(,这一个是减号,switch查表,发现是小于号,直接入栈,读取下一个,接着继续读取
发现是7,是操作数,入栈。接着继续读取
读取之后发现是ch=),然后与栈顶元素比较,发现是大于号,那么进行计算操作,如下:
首先读取操作符然后弹出操作符,接着读取操作数栈中的两个元素,a是后面那一个元素(7),b是前面那一个元素(9),执行操作b操作符a,返回计算值,并入操作数栈
注意,此时ch=)没有改变,然后接着进行switch查表,发现是=,进行弹出操作,将括号删除,读取下一个元素。
思考:如果这时候后面有一个加号(减号、乘号、除号),算法成立吗?思考好了这个问题相信就能明白为什么计算的那一步为什么不直接接着读取下一个元素了。
读取下一个元素,发现是=,与栈顶元素比较,发现前面是
,查表结果是大于号,进行运算,运算过程略过。然后进行查表,发现又是=,然后他要getchar,当然这时候getchar到的是我们的回车了,但是我们根据getchar的特点,getchar会吸收回车,但是是作为结束标志,而不是作为一个读取的字符进行赋值,所以这里不会对我们的c造成影响,仍然是=。
之后回到我们的循环头部,发现这时候到了栈顶为‘=’,读取到的又是‘=’的情况,触发结束条件,结束。
综上模拟一遍,我们就知道这个算法应该怎么实现了。

①首先,我们需要写出一个函数,来获取每一个操作符在表中的位置
②第二,我们需要写出一个函数,其中包含表的信息,可以调用第一个函数来帮助我们查表
③第三,需要写出主函数部分,这个函数的处理包括两个部分:第一部分用来处理操作数,第二部分用来处理操作符。
第一部分:操作数包括操作数的入栈,多位运算的功能
第二部分:操作符包括查表操作,以及上述过程中提到的任意操作

代码分解分析:
第一部分,获取操作符位置的函数

int GetIndex(char c)//查找操作符在表中的位置,以便比较
{
    int index=0;
    switch(c)
    {
    case'+':
        index=0;
        break;
    case'-':
        index=1;
        break;
    case'*':
        index=2;
        break;
    case'/':
        index=3;
        break;
    case'(':
        index=4;
        break;
    case')':
        index=5;
        break;
    case'=':
        index=6;
        break;
    default:break;
    }
    return index;//这个代码是有缺陷的,如果读入的是@¥% 等无关的字符的时候,这里返回的index就默认为+,可是OJ上并没有这样的测试数据。

}

第二部分,表的实现

char GetPriority(char theta1,char theta2)
{
    const char priority[][7] =     //算符间的优先级关系
	{
		{ '>','>','<','<','<','>','>' },
		{ '>','>','<','<','<','>','>' },
		{ '>','>','>','>','<','>','>' },
		{ '>','>','>','>','<','>','>' },
		{ '<','<','<','<','<','=','0' },
		{ '>','>','>','>','0','>','>' },
		{ '<','<','<','<','<','0','=' },
	};
	int index1=GetIndex(theta1);
	int index2=GetIndex(theta2);
	return priority[index1][index2];
	//将返回两个的优先级关系
}

第三部分,计算两个数

double operate(double b,char c,double a)//注意模拟的时候要注意顺序
{
    switch(c)
    {
    case'+':
        return a+b;
    case'-':
        return b-a;
    case'*':
        return b*a;
    case'/'://注意这个代码仍然是有缺陷的,因为不保证a不是0,会出现-nan这样的东西
        return b/a;
    default:
        break;
    }
}

主函数部分:
函数头:

    //操作符栈是opter,操作数栈是operand
    opter.push('=');//这里说明一下为什么先压=入栈,=是一个本次操作是否结束的标志,开始的时候由于读入的不是=,是当前栈顶元素是=,所以不会引起结束
    int counter=0;//用于计算每一个数的单位
    char c=getchar();

操作数部分:

        if(isdigit(c))//是不是数字字符,digit函数是用来bool一个数是否是一个十进制数的
        {
            if(counter==1)//函数头规定counter=0,如果上一个数也是数字,那么就会进这里,然后就可以把多个字符类型的数字变成int型的
            {
                double t=operand.top();
                operand.pop();
                operand.push(t*10+(c-'0'));
                counter=1;
            }
            else//第一个数会进这里
            {
                operand.push(c-'0');
                counter++;//然后准备下一个数
            }
            c=getchar();//处理完这个数了,进行getchar准备下一个数
           }

操作符部分:

        else
        {
            counter=0;
            switch(GetPriority((opter.top()),c))
                   {
                   case '<':
                    opter.push(c);
                    c=getchar();
                    break;
                   case'=':
                    opter.pop();//优先表中只有()会出现等号,所以这个是用来消除等号的
                    c=getchar();
                    break;
                   case'>'://如果小于了,那么在这一步需要计算
                    char theta=opter.top();
                    opter.pop();
                    double a=operand.top();
                    operand.pop();
                    double b=operand.top();
                    operand.pop();
                    operand.push(operate(b,theta,a));
                   }
        }

完整主函数:


double GetAns()
{
    //操作符栈是opter,操作数栈是operand
    opter.push('=');//这里说明一下为什么先压=入栈,=是一个本次操作是否结束的标志,开始的时候由于读入的不是=,是当前栈顶元素是=,所以不会引起结束
    int counter=0;//用于计算每一个数的单位
    char c=getchar();
    while(c!='='||opter.top()!='=')
    {
        if(isdigit(c))//是不是数字字符
        {
            if(counter==1)
            {
                double t=operand.top();
                operand.pop();
                operand.push(t*10+(c-'0'));
                counter=1;
            }
            else
            {
                operand.push(c-'0');
                counter++;
            }
            c=getchar();
        }
        else
        {
            counter=0;
            switch(GetPriority((opter.top()),c))
                   {
                   case '<':
                    opter.push(c);
                    c=getchar();
                    break;
                   case'=':
                    opter.pop();//优先表中只有()会出现等号,所以这个是用来消除等号的
                    c=getchar();
                    break;
                   case'>'://如果小于了,那么在这一步需要计算
                    char theta=opter.top();
                    opter.pop();
                    double a=operand.top();
                    operand.pop();
                    double b=operand.top();
                    operand.pop();
                    operand.push(operate(b,theta,a));
                   }
        }
    }
    return operand.top();
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值