栈的经典应用问题:表达式求值,传统做法,C++代码(LeetCode 227. Basic Calculator II,224. Basic Calculator)

给定一个字符串形式表达式,不使用内置库,将表达式的值求出。

224题只考虑表达式内含有+ - ( ),而227题只考虑表达式内有+ - * / 没有考虑括号的问题,这里打算写出一个考虑+ - * / ( )的解决方案,把两题一起做了,这里的-号是减号,不是负号,也不考虑负数。

224. Basic Calculator

Implement a basic calculator to evaluate a simple expression string.

The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces .

Example 1:
Input: "1 + 1"
Output: 2

Example 2:
Input: " 2-1 + 2 "
Output: 3

Example 3:
Input: "(1+(4+5+2)-3)+(6+8)"
Output: 23

Note:
You may assume that the given expression is always valid.
Do not use the eval built-in library function.




227. Basic Calculator II

Implement a basic calculator to evaluate a simple expression string.

The expression string contains only non-negative integers, +, -, *, / operators and empty spaces . The integer division should truncate toward zero.

Example 1:
Input: "3+2*2"
Output: 7

Example 2:
Input: " 3/2 "
Output: 1

Example 3:
Input: " 3+5 / 2 "
Output: 5

Note:
You may assume that the given expression is always valid.
Do not use the eval built-in library function.


思路:
我的思路是比较常规,或者说比较笨的方法。

首先表达式按操作符在操作数的中间,还是在操作数的后面可以分为中缀表达式后缀表达式。比如1 + 1就是一个中缀表达式,而1 1 +则是一个后缀表达式。中缀表达式便于日常使用,但是使用中缀表达式在计算机内求一个表达式值的时候需要考虑优先级,比如* / 法比 + -法的优先级要高。而如果使用后缀表达式求表达式的值时,不需要考虑运算符的优先级,可以很方便地计算,详细计算过程在后面

所以这题的思路是先考虑将中缀表达式转换为后缀表达式,然后再后缀表达式的基础上求表达式的值。

中缀表达式转换为后缀表达式。这里采用使用栈的方法来进行。
算法思想如下:
从左向右扫描整个表达式

  1. 如果遇到数字直接将表达式加入后缀表达式
  2. 遇到运算符时
  3. 如果遇到 左括号"(",则将左括号入栈
  4. 如果遇到右括号")", 则将栈内操作符逐一出栈,并且加入后缀表达式,直到遇到左括号"(",并且将左括号删除(出栈)
  5. 否则遇到的是+ - * /,这时如果栈空直接将运算符入栈,如果栈不空,则取出栈顶操作符op1和当前扫描到的操作符进行比较op2,如果优先级op1<op2则将op2入栈,否则有op1>=op2弹出当前栈顶操作符并加入后缀表达式,重复这个过程直到栈空,或者遇到左括号"(",或者栈顶元素优先级比当前扫描到的操作符优先级小op1<op2,这时再将op2入栈(如果有op1入栈必须保证有op1 < op2

以上就是中缀表达式转后缀表达式的思路,这部分代码如下
中缀表达式转后缀表达式代码,后面为后缀表达式求值过程

int priority(string s1, string s2)//比较两个操作符的优先级,s1表示栈顶操作符,s2表示新到的操作符
{//如果s2优先级比s1大返回正数,相等返回0,否则返回负数
    //if (s1 == "#" )//'#'表示开始,任何运算符都比它的优先级低
    //    return -1;
    if (s2 == "(")//直接入栈
        return 1;
    if (s1 == "(")//当弹出栈内操作符遇到"("时,应该让新来的操作符入栈
        return 1;
    else if (s1 == s2)
        return 0;
    else if (s1 == "+" && s2 == "-" || s1 == "-" && s2 == "+")
        return 0;
    else if (s1 == "*" && s2 == "/" || s1 == "/" && s2 == "*")
        return 0;
    else if ( (s1 == "+" || s1 == "-") && (s2 == "*" || s2 =="/") )
        return 1;
    else //if ( (s1 == "*" || s1 == "/") && (s2 == "+" || s2 =="-") )
        return -1;

}

//input = "24+2*6/(3-1)" output = (24+((2*6)/(3-1)))-->24 2 6 * 3 1 - / +
vector<string> Infix_expression_to_suffix_expression(const vector<string>& tokens)
{
    vector<string> suffix_expression;
    stack<string> stk_2;    //操作符栈
    for(int i = 0; i<tokens.size(); i++)//遇到操作数,直接入栈
    {
        if (isdigit(tokens[i][0]) || tokens[i].length()>1)//数字
        {
            suffix_expression.push_back(tokens[i]);//如果是数字直接加入后缀表达式
        }
        else//遇到操作符
        {
            if(tokens[i] == ")")//如果运算符是右括号,将栈顶元素出栈直到遇到"(",并且将左括号删除
            {
                while(stk_2.top() != "(")
                {
                    suffix_expression.push_back(stk_2.top());
                    stk_2.pop();
                }
                stk_2.pop();//删除"("
                continue;
            }

            if (stk_2.empty())//第一个操作符直接入栈
            {
                stk_2.push(tokens[i]);
                continue;
            }
            string op1 = stk_2.top();//第一个操作符
            string op2 = tokens[i];//第二个操作符
            int prio = priority(op1, op2);
            if(prio > 0)//新来的操作符比栈顶操作符优先级高,直接把新来的操作符入栈
            {
                stk_2.push(tokens[i]);
            }
            else
            {
                while(priority(op1, op2) <= 0)//栈顶操作符优先级高,让栈顶操作符出栈加入后缀表达式直到栈顶操作符优先级比新来的操作符优先级低
                {
                    suffix_expression.push_back(stk_2.top());
                    stk_2.pop();
                    if (!stk_2.empty())
                        op1 = stk_2.top();
                    else break;//栈内所有操作符都比新来的操作符优先级低,导致栈内所有运算符都出栈了
                }
                stk_2.push(tokens[i]);
            }
        }
    }

    while(!stk_2.empty())//将栈内还剩下的操作符加入后缀表达式
    {
        suffix_expression.push_back(stk_2.top());
        stk_2.pop();
    }

    return suffix_expression;
}



接下来就是计算后缀表达式的值,这个过程非常简单:
后缀表达式计算思路:后缀表达式计算主要需要使用栈,建立一个栈stk 。从左到右读表达式,如果读到操作数就将它压入栈stk,如果读到n元运算符(即需要参数个数为n的运算符)则取出由栈顶向下的n项操作数,按操作符运算,再将运算的结果代替原栈顶的n项,压入栈S中 。如果后缀表达式未读完,则重复上面过程,最后输出栈顶的数值则为结束(详细的过程可以看我的这篇博客LeetCode 150. Evaluate Reverse Polish Notation(后缀表达式)




完整代码:

class Solution {
public:
    int calculate(string s) {
        while(s.find(" ") != string::npos)//去除空格
        {
            s.replace(s.find(" "), 1, "");
        }
        vector<string> tok = tokenize_expression(s);
        vector<string> suffix_tok = Infix_expression_to_suffix_expression(tok);
        int r = evalRPN(suffix_tok);
        return r;
    }
    
    int compute(int opd1, int opd2, string op)
    {
        if(op == "+")
            return opd1 + opd2;
        else if(op == "-")
            return opd1 - opd2;
        else if(op == "*")
            return opd1 * opd2;
        else// if(op == "/")
            return opd1 / opd2;
    }
    
    int evalRPN(vector<string>& tokens) {
        stack<int> stk_1;//操作数栈

        for(int i = 0; i<tokens.size(); i++)
        {
            if ( isdigit(tokens[i][0]) || tokens[i].length()>1 )//遇到了数字直接入栈
            {
                stk_1.push( stoi(tokens[i]) );//c++11 string to interge
            }
            else//遇到了操作符
            {
                string op = tokens[i];//栈顶元素

                int opd2 = stk_1.top();stk_1.pop();//注意先出栈的是右操作数
                int opd1 = stk_1.top();stk_1.pop();
                int result = compute(opd1, opd2, op);
                stk_1.push(result);//计算结果入操作数栈

            }
        }//end for loop

        return stk_1.top();
    }
    
    //for example input = "24+2*6/(3-1)" from string to vector<string>
    vector<string> tokenize_expression(const string& expression)//(64-33)/2-1
    {
        vector<string> tokens;
        int start_index = 0, end_index = 0;
        for(int i = 0; i<expression.length(); i++)
        {
            if(!isdigit(expression[i]))
            {
                end_index = i;
                if(end_index - start_index > 0)//最后一个元素
                    tokens.push_back(expression.substr(start_index, end_index-start_index));//提取两个运算符夹着的数字
                tokens.push_back(expression.substr(end_index, 1));//提取运算符
                //start_index 和 end_index 指向下一个位置
                start_index = end_index+1;
            }
            else if(i == expression.length()-1)//最后一个是数字
            {
                end_index = expression.length();
                tokens.push_back(expression.substr(start_index, end_index-start_index));
            }
        }
        return tokens;
    }
    
    int priority(string s1, string s2)//比较两个操作符的优先级,s1表示栈顶操作符,s2表示新到的操作符
    {//如果s2优先级比s1大返回正数,相等返回0,否则返回负数
        //if (s1 == "#" )//'#'表示开始,任何运算符都比它的优先级低
        //    return -1;
        if (s2 == "(")//直接入栈
            return 1;
        if (s1 == "(")//当弹出栈内操作符遇到"("时,应该让新来的操作符入栈
            return 1;
        else if (s1 == s2)
            return 0;
        else if (s1 == "+" && s2 == "-" || s1 == "-" && s2 == "+")
            return 0;
        else if (s1 == "*" && s2 == "/" || s1 == "/" && s2 == "*")
            return 0;
        else if ( (s1 == "+" || s1 == "-") && (s2 == "*" || s2 =="/") )
            return 1;
        else //if ( (s1 == "*" || s1 == "/") && (s2 == "+" || s2 =="-") )
            return -1;

    }
    
    vector<string> Infix_expression_to_suffix_expression(const vector<string>& tokens)
    {
        vector<string> suffix_expression;
        stack<string> stk_2;    //操作符栈
        for(int i = 0; i<tokens.size(); i++)//遇到操作数,直接入栈
        {
            if (isdigit(tokens[i][0]) || tokens[i].length()>1)//数字
            {
                suffix_expression.push_back(tokens[i]);//如果是数字直接加入后缀表达式
            }
            else//遇到操作符
            {
                if(tokens[i] == ")")//如果运算符是右括号,将栈顶元素出栈直到遇到"(",并且将左括号删除
                {
                    while(stk_2.top() != "(")
                    {
                        suffix_expression.push_back(stk_2.top());
                        stk_2.pop();
                    }
                    stk_2.pop();//删除"("
                    continue;
                }

                if (stk_2.empty())//第一个操作符直接入栈
                {
                    stk_2.push(tokens[i]);
                    continue;
                }
                string op1 = stk_2.top();//第一个操作符
                string op2 = tokens[i];//第二个操作符
                int prio = priority(op1, op2);
                if(prio > 0)//新来的操作符比栈顶操作符优先级高,直接把新来的操作符入栈
                {
                    stk_2.push(tokens[i]);
                }
                else
                {
                    while(priority(op1, op2) <= 0)//栈顶操作符优先级高,让栈顶操作符出栈加入后缀表达式直到栈顶操作符优先级比新来的操作符优先级低
                    {
                        suffix_expression.push_back(stk_2.top());
                        stk_2.pop();
                        if (!stk_2.empty())
                            op1 = stk_2.top();
                        else break;//栈内所有操作符都比新来的操作符优先级低,导致栈内所有运算符都出栈了
                    }
                    stk_2.push(tokens[i]);
                }
            }
        }

        while(!stk_2.empty())//将栈内还剩下的操作符加入后缀表达式
        {
            suffix_expression.push_back(stk_2.top());
            stk_2.pop();
        }

        return suffix_expression;
    }
    
};

224题结果
在这里插入图片描述

227题结果
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值