计算表达式的结果 双端队列结构的应用

题意:

给定一个字符串str,str表示一个公式,公式里可能有整数、加减乘除符号和
左右括号,返回公式的计算结果。
【举例】
str="48*((70-65)-43)+8*1"
,返回-1816。
str="3+1*4"
,返回7。 str="3+(1*4)"
,返回7。

【说明】
1.可以认为给定的字符串一定是正确的公式,即不需要对str做公式有效性检
查。
2.如果是负数,就需要用括号括起来,比如"4*(-3)"。但如果负数作为公式的
开头或括号部分的开头,则可以没有括号,比如"-3*4"和"(-3*4)"都是合法的。
3.不用考虑计算过程中会发生溢出的情况

思路:

我们用双端队列来做这道题目,首先将str分成两种情况,并讨论他们的解法

情况一:

给定的表达式str中没有括号,只有‘+’  ‘-’  ‘*’  ‘/’  ,这样我们定义一个栈结构(双端队列也可以),假定给的表达式为"4+5+9*8",让我们计算这个表达式的结果。注意,栈中存的是string类型。

一:先将4放入到栈中去。

二:将‘+’放入栈中

三:将5放入到栈中去,但是要检查此时栈顶的元素所代表的符号是否为‘+’或者‘-’,如果是,直接加,如果是‘*’或者‘/’,和前一个数字计算完成后再加

四:将‘+’放入到栈中去

五:将9放入到栈中去,但是要检查此时栈顶的元素所代表的符号是否为‘+’或者‘-’,如果是,直接加,如果是‘*’或者‘/’,和前一个数字计算完成后再加

六:将‘*’放入到栈中去

七:将8放入到栈中去,但是要检查此时栈顶的元素所代表的符号是否为‘+’或者‘-’,如果是,直接加,如果是‘*’或者‘/’,和前一个数字计算完成后再加

八:最后计算栈中这个表达式就可以了。

情况二:

表达式中有’(‘和’)',我们设置一个递归函数,依次访问str字符串中的每个位置上的字符,如果遇到'('时,我们就调用递归函数,让下一级递归函数给我们计算出括号内的表达式的结果和下一个要遍历的位置,关于如何计算括号内的表达式的结果,用情况一的那种计算方法,具体操作看代码。

代码:

#include<algorithm>
#include<iostream>
#include<limits.h>
#include <sstream>
#include<stdio.h>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#define mod 1000000007
#define MAXN 10000001
typedef long long ll;
using namespace std;
string root;
struct Node//递归函数的返回值类型
{
    int index,num;//index代表与'('对应的')'的位置,num代表括号内的计算结果
    Node(int index,int num):index(index),num(num){};
};
int StringToInt(string op)//string类型变成int类型
{
    int a;
    istringstream issInt(op);
    issInt>>a;
    return a;
}
string IntToString(int i)//int类型变成string类型
{
    stringstream streamStr;
    string z4;
    streamStr<<i;
    streamStr>>z4;
    return z4;
}
string CharToString(char op)//char类型变成string类型,往双端队列中加单个字符时用
{
    string str;
    stringstream stream;
    stream<<op;
    str = stream.str();
    return str;
}
void Addnum(deque <string>& q,int num)//这是往双端队列中加入数字的函数,必须得自己写,因为有乘  除的存在
{
    if(!q.empty())//只要是双端队列不为空,就得判断乘  除的情况,如果双端队列为空,直接加就行
    {
        string z=q.back();//得到要加入的数字的前一个符号
        q.pop_back();
        if(z=="+"||z=="-")//如果为+  - 我们直接加入这个数进双端队列中去就可以了
            q.push_back(z);
        else              //否则,为乘  除 ,我们要先算出来结果,然后把结果加入到双端队列中去
        {
            int number=StringToInt(q.back());
            q.pop_back();
            num=(z=="*")?num*number:number/num;//算结果
        }
    }
    q.push_back(IntToString(num));//将数字加入到双端队列中去
}
int GetNum(deque <string>& q)//这个函数是计算双端队列中存的那个计算式的结果的函数
{
    int num=0;
    bool v=true;//如果是下面的代码,那么这个地方一定要赋为True
    while(!q.empty())//依次弹出每个符号(有可能是数字符号),计算结果
    {
        string z=q.front();
        q.pop_front();
        if(z=="+")
            v=true;
        else if(z=="-")
            v=false;
        else
        {
            int number=StringToInt(z);
            num+=v?number:-number;
        }
    }
    return num;//返回这个计算结果
}
Node *Conversion(int Index)//这是递归函数,如果遇到’(‘,我们就递归进入下一级,让下一级计算出括号内的结果,然后返回这个结果 和 与'('对应的')'的位置
{
    deque < string > q;
    int Num=0;
    while (root[Index]!=')'&&Index<root.size())
    {
        if(root[Index]>='0'&&root[Index]<='9')//当前位置的字符是数字
            Num=Num*10+root[Index++]-'0';
        else if(root[Index]!='(')//遇到了'+' '-' '*' '/',说明这部分的数字已经收集完了,
        {
            if(Index>=root.size())//这个是边界的处理,访问到字符串的最后的位置时候会用到
                break ;
            Addnum(q,Num);//往双端队列中加入收集到的数字
            string str=CharToString(root[Index]);
            q.push_back(str);//加入这个符号
            Num=0;//清零  收集下一个数字
            Index++;
        }
        else//遇到了'('
        {
            Node *Return=Conversion(Index+1);//调用递归函数,从'('所在位置的下一个位置开始计算,一直到与之对应的')'位置
            Num=Return->num;//此时Num就代表括号内的计算结果
            Index=Return->index;//得到下一个访问位置
        }
    }
    Addnum(q,Num);//最后的时候只有数字,没有运算符号了,必须要把这个数字加入到双端队列中去,这也算是一个边界处理
    Node *node = new Node(Index+1,GetNum(q));
    return node;//返回给上一级相应的信息
}
int main()
{
    cin>>root;
    Node *node=Conversion(0);
    cout<<node->num<<endl;
}

猜你喜欢

转载自blog.csdn.net/qq_40938077/article/details/83933420