使用四个双端队列+辅助栈

题目描述

链接:https://ac.nowcoder.com/acm/problem/50999 来源:牛客网

给出一个表达式,其中运算符仅包含+,-,*,/,^(加 减 乘 整除 乘方)要求求出表达式的最终值数据。可能会出现括号情况,还有可能出现多余括号情况,数据保证不会出现 x31x^{31}的答案,数据可能会出现负数情况。

输入描述:

仅一行,即为表达式

输出描述

仅一行,既为表达式算出的结果

思路

使用双端队列,其中

  1.  std::deque<int> numsQueue用来存储表达式中的数字;
    
  2.  std::deque<char> charQueue用来存储除括号以外的所有运算符;
    
  3.  std::deque<int> helpNumQueue;std::deque<char> helpCharQueue;
     用来处理作为最高优先级运算符的括号的辅助队列;
    
  4.  std::stack<int> record; 
     记录一对括号中参与运算后的数字个数,在使用辅助队列时从队列弹回相应个数的数字;
    
  5.  std::stack<char> parenthessRecord;
     记录左括号的使用情况,当栈里只有一个‘(’,并且之后遇到的第一个括号是‘)’时,弹出‘(’;
    
  6.  int parentheses;
     记录成对括号的使用情况,遇到‘(’记录加1,遇到‘)’记录减一。
    

优先级处理,除括号之外,乘方的优先级最高,其次为乘除,最后为加减。计算乘方、乘除、加减时分别使用了三个函数:

  •   void insideParenthPow()
      计算乘方
    
  •   void insideParenth1()
      计算乘除
    
  •   void insideParenth2()
      计算加减
    

每次对括号内的表达式进行计算时使用bool hasOperational(char ch)函数判断charQueue中有无目标运算符,先检查乘方运算符,其次是乘除,前两者计算完毕后必然剩下加减运算符,根据返回的条件开展计算,最终使用void insideParentTotal()得到括号内的运算结果:

      void insideParentTotal(){
        if(this->hasOperational('^')) this->insideParenthPow();
        if(this->hasOperational('*')||this->hasOperational('/')){
            this->insideParenth1();
        }
        this->insideParenth2();
    }

对题目中的负数的处理:遍历输入字符串中的‘-’,若是字符串前面不为数字,且不为右括号‘)’,则在‘-’前面加上一个字符‘0’。函数实现如下。

  std::string addZero(std::string aString){
        int length=aString.size();
        std::string temp;
        for(int i=0;i<length;i++){
            if(i==0){
                if(aString[i]=='-') temp+='0';
            }
            else{
                if(aString[i]=='-'
                   		&&!this->isNumber(aString[i-1])&&aString[i-1]!=')') 
  					temp+='0';
            }
            temp+=aString[i];
        }
        return temp;
    }

一些辅助函数:

  •   	bool isNumber(char ch)
      	判断字符是否为数字
    
  •   	void printNumsQueue()
      	打印当前std::deque<int> numsQueue中的内容
    
  •   	void printCharQueue()
      	打印当前std::deque<char> charQueue中的内容
    
  •   	std::string getNumber(int &start,std::string expression)
      	从字符串中提取数字
    
  •   	void getTrueNumber(std::string expression,char ch,int &i)
      	函数命名和作用严重不符合,函数实现将数字和运算符分别放入对应的队列中,遇到‘(’或
      	‘)’结束
    
  •   	void toHelpQueue(int lengthOfNumQueue)
      	将当前队列中的数字和运算符按照自定义长度放入辅助队列中
    
  •   	void toUsingQueue(int lengthOfHelpQueue)
      	将辅助队列中的数字和运算符按照自定义的长度放入当前的队列中
    
  •   	void NotAddAndDiff(int queueCount)
      	void insideParenth1()中使用的函数,实现对当前队列中数字的乘除运算;
    

    最终,使用int calculate(std::string expression)函数遍历一遍处理过符号‘-’的字符串,对括号和相关的数字和运算符进行计算,并返回最后的计算结果。完整代码如下:

//#include <bits/stdc++.h>
#include <iostream>
#include <string>
#include <deque>
#include <stack>
#include <cmath>

class mySolution{
private:
    std::deque<int> numsQueue;
    std::deque<char> charQueue; 
    std::deque<int> helpNumQueue;
    std::deque<char> helpCharQueue;
    std::stack<int> record;
    std::stack<char> parenthessRecord;
    int parentheses;
    
public:
    bool isNumber(char ch){
        if(ch>='0'&&ch<='9') return true;
        else return false;
    }
    
    std::string addZero(std::string aString){
        int length=aString.size();
        std::string temp;
        for(int i=0;i<length;i++){
            if(i==0){
                if(aString[i]=='-') temp+='0';
            }
            else{
                if(aString[i]=='-'&&!this->isNumber(aString[i-1])&&aString[i-1]!=')') temp+='0';
            }
            temp+=aString[i];
        }
        return temp;
    }
        
    void printNumsQueue(){
        int length=this->numsQueue.size();
        while(length--){
            std::cout<<this->numsQueue.front()<<" ";
            this->numsQueue.push_back(this->numsQueue.front());
            this->numsQueue.pop_front();
        }
        std::cout<<std::endl;
    }
    
    void printCharQueue(){
        int length=this->charQueue.size();
        while(length--){
            std::cout<<this->charQueue.front()<<" ";
            this->charQueue.push_back(this->charQueue.front());
            this->charQueue.pop_front();
        }
        std::cout<<std::endl;
    }
    
    std::string getNumber(int &start,std::string expression){
        int originStart=start;
        int length=expression.size();
        while(this->isNumber(expression[start])&&start<length){
            start++;
        }
        auto temp=expression.substr(originStart,start-originStart);
        return temp;
    }
    
    void getTrueNumber(std::string expression,char ch,int &i){
        int length=expression.size();
        while(expression[i]!=ch&&i<length){
            if(this->isNumber(expression[i])){
                auto nums=std::stoi(this->getNumber(i,expression));
                this->numsQueue.push_back(nums);
            }
            if(expression[i]=='('||expression[i]==')') break;
            if(!this->isNumber(expression[i])&&i<length){
                this->charQueue.push_back(expression[i]);
            }
            i++;
        }
    }
     
    void NotAddAndDiff(int queueCount){
        int temp=this->numsQueue.front();
        numsQueue.pop_front();
        while(!this->charQueue.empty()&&this->charQueue.front()!='+'&&this->charQueue.front()!='-'){
            if(this->charQueue.front()=='*') temp*=numsQueue.front();
            else if(this->charQueue.front()=='/') temp/=numsQueue.front();
            this->numsQueue.pop_front();
            this->charQueue.pop_front();
        }
        this->numsQueue.push_front(temp);       
        for(int i=0;i<queueCount;i++){
            this->charQueue.push_front(this->charQueue.back());
            this->numsQueue.push_front(this->numsQueue.back());
            this->charQueue.pop_back(); 
            this->numsQueue.pop_back();
        }         
    }
    
    bool hasOperational(char ch){
        int length=this->charQueue.size();
        bool has=false;
        while(length--){
            this->charQueue.push_back(this->charQueue.front());
            if(ch==this->charQueue.front()) has=true;
            this->charQueue.pop_front();
        }
        return has;
    }
    
    void insideParenthPow(){
        int size=charQueue.size();
        int queueCount=0;
        while(this->hasOperational('^')){
            if(this->charQueue.front()!='^'){
                this->charQueue.push_back( this->charQueue.front());
                this->charQueue.pop_front();
                this->numsQueue.push_back( this->numsQueue.front());
                this->numsQueue.pop_front();
                queueCount++;
            }
            else{
                int temp=this->numsQueue.front();
                numsQueue.pop_front();               
                while(!this->charQueue.empty()&&this->charQueue.front()=='^'){
                     temp=std::pow(temp,this->numsQueue.front());
                     this->numsQueue.pop_front();
                     this->charQueue.pop_front();
                 }
                this->numsQueue.push_front(temp);
                
                for(int i=0;i<queueCount;i++){
                    this->charQueue.push_front(this->charQueue.back());
                    this->numsQueue.push_front(this->numsQueue.back());
                    this->charQueue.pop_back(); 
                    this->numsQueue.pop_back();
                }
                queueCount=0;           
            }
        }
    }
    
    void insideParenth1(){
        int queueCount=0;
        while(this->hasOperational('*')||this->hasOperational('/')){
            if(this->charQueue.front()!='*'&&this->charQueue.front()!='/'){
                this->charQueue.push_back( this->charQueue.front());
                this->charQueue.pop_front();
                this->numsQueue.push_back( this->numsQueue.front());
                this->numsQueue.pop_front();
                queueCount++;
            }
            else{
                this->NotAddAndDiff(queueCount);
                queueCount=0;  
            }
        }
    }
    
    void insideParenth2(){
        int sum=this->numsQueue.front();
        this->numsQueue.pop_front();
        while(!charQueue.empty()){
            if(this->charQueue.front()=='+') sum+=this->numsQueue.front();
            else sum-=this->numsQueue.front();
            this->numsQueue.pop_front();
            this->charQueue.pop_front();
        }
        this->numsQueue.push_front(sum);
    }
    
    void insideParentTotal(){
        //1111111=========================
        //this->printNumsQueue();
        //this->printCharQueue();
        
        if(this->hasOperational('^')) this->insideParenthPow();
        //222222=========================
        //this->printNumsQueue();
        //this->printCharQueue();
        
        if(this->hasOperational('*')||this->hasOperational('/')){
            this->insideParenth1();
        }
        //333333333=========================
        //this->printNumsQueue();
        //this->printCharQueue();
        
        this->insideParenth2(); 
        
        //44444444444=========================
        //this->printNumsQueue();
        //this->printCharQueue();
    }
    
    void toHelpQueue(int lengthOfNumQueue){
        while(lengthOfNumQueue--){
            this->helpNumQueue.push_back(this->numsQueue.front());
            this->helpCharQueue.push_back(this->charQueue.front());
            this->numsQueue.pop_front();
            this->charQueue.pop_front();
        }
    }
    
    void toUsingQueue(int lengthOfHelpQueue){
        while(lengthOfHelpQueue--){
            this->numsQueue.push_front(this->helpNumQueue.back());
            this->charQueue.push_front(this->helpCharQueue.back());
            this->helpNumQueue.pop_back();
            this->helpCharQueue.pop_back();
        }
    }

    int calculate(std::string expression){
        expression=this->addZero(expression);
        int length=expression.size();
        int i=0;
        while(i<length){
            if(expression[i]=='('){
                while(expression[i]=='(') {
                    this->parenthessRecord.push('(');
                    this->parentheses++;
                    i++;
                }
                this->getTrueNumber(expression,')', i);
                if(expression[i]=='('){
                    this->record.push(this->numsQueue.size());
                    this->toHelpQueue(this->numsQueue.size());
                } 
                else{
                    this->insideParentTotal();
                    while(expression[i]==')') {
                        if(this->parenthessRecord.size()==1) this->parenthessRecord.pop();
                        this->parentheses--;i++;
                    }
                    if(this->parentheses<=0){
                        if(!this->record.empty()){
                            int temp=this->record.top(); this->record.pop();
                            this->toUsingQueue(temp);
                            if(this->parentheses==0){
                                if(i<=length&&!this->parenthessRecord.empty()) 
                                {    this->insideParentTotal();  }
                                while(!this->parenthessRecord.empty()){
                                    this->parenthessRecord.pop();
                                }
                            }
                        }
                    } 
                }
            }
            if(expression[i]!='('){
                this->getTrueNumber(expression,'(', i);
                if(expression[i]=='('){
                    this->record.push(this->numsQueue.size());
                    this->toHelpQueue(this->numsQueue.size());
                }
                if(expression[i]!='('){
                    this->insideParentTotal();
                    if(expression[i]==')'){
                        if(this->parenthessRecord.size()==1) this->parenthessRecord.pop();
                        this->parentheses--;
                    } 
                    i++;
                }
            }
        }
        while(!this->helpNumQueue.empty()){
            while(!this->record.empty()){
                int temp=this->record.top(); this->record.pop();
                this->toUsingQueue(temp);
                this->insideParentTotal();
            }
        }
        this->insideParentTotal();
        return this->numsQueue.front();
    }                 
};

int main(){
    mySolution*solution=new mySolution();
    std::string expression;
    while(std::cin>>expression){
        int answer=solution->calculate(expression);
        std::cout<<answer<<std::endl;
    }
    delete solution;
    return 0;
}
 

其实我觉得代码应该更精简,逻辑更清晰,其中最难处理的是多余的括号,以至于我不断调试,使用大佬们的代码找通不过的案例,最后弄得我极其混乱,决定不再挣扎之后,竟然让我发现了处理‘-’号时,前面有‘)’时就不要加‘0’了这个最后通不过的17.7%,然后又又又加了限制条件之后居然通过了!喜大普奔!所以我决定写一个题解来记录一下这个平凡的下午。 大佬们还是强!另外,确实,这个多余的括号是怎么分类的呢?测试一位大佬简洁的递归代码,(5)2)))))))))))))))))+2(-5)^2)))))))))))))))))+2这个示例是通不过的,算出来不是27,但是他的代码可以完美通过。 牛客为什么不能像力扣那样,直接显示通不过的案例呢,这样也方便调试,不用自己穷尽思路想案例!