Leetcode--面试题16.26.计算器【C++、栈的应用】


这道题是 逆波兰表达式求值的一个延伸。
在解上述这道题时,明显在后缀表达式的计算中,不用考虑运算符优先级的问题。程序运行时间跟问题规模是线性关系,即时间复杂度是O(N)。而在下面这道题中,要实现简单计算器的功能,则要考虑运算符的优先级的问题,还有着运算结合顺序的问题。

题目描述

给定一个包含正整数、加(+)、减(-)、乘()、除(/)的算数表达式(括号除外),计算其结果。
表达式仅包含非负整数,+, - ,
,/ 四种运算符和空格 。 整数除法仅保留整数部分。

示例 1:
输入: “3+2*2” 输出: 7
示例 2:
输入: " 3/2 " 输出: 1
示例 3:
输入: " 3+5 / 2 " 输出: 5
说明:
你可以假设所给定的表达式都是有效的。请不要使用内置的库函数 eval。

题解(不考虑有括号的情况)

思路:先将中缀表达式 转换为后缀表达式,然后再利用后缀表达式求值。
问题就困难在 如何将中缀表达式转换为后缀表达式?

  • 通过举例可以发现 转换前和转换后的表达式中 运算数的相对顺序没有变,改变的是操作符的相对顺序
  • 想到用 栈 来存储暂时等待中的运算符,将当前的运算符与栈顶的运算符做比较,来决定弹出还是压入
    步骤:
  • 从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况进行处理
  • 因此需要用到一个数组来存储转换后的后缀表达式,用一个栈来存储运算符
  • 运算数:直接插入结果数组中,其后加 ' ',这里其实要注意使用while循环读入运算数,因为运算数有不是1位数的情况,读入完整运算数后再在其后加空格 。同样地 在ecalculateRPN函数模块中读取运算数时也应该注意,使用while循环将字符转换为整型
  • 运算符:如果当前栈为空,则直接压入栈中,否则要比较当前运算符与栈顶运算符的优先级
  • 如果优先级 > 栈顶运算符 压入栈中
  • 如果优先级 <=栈顶运算符 将栈顶运算符弹出并输出到结果数组中,其后加 ' '。再比较新的栈顶运算符 直到该运算符优先级> 栈顶运算符为止然后将该运算符压入栈。
  • 如果各对象处理完毕,则把栈中的运算符一一输出到结果数组中,其后加 ' '
  • 在结果数组中加空格的目的是 将 运算数和 运算符隔开,方便在ecalculateRPN模块中对运算数和运算符的判定添加便利
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <cstdlib>
#include <cstring>

using namespace std;

int calculate(string s);
vector<char> toRPN(string s);//注意这里 是把后缀表达式存储为 char类型的vector数组中,不再是Leetcode150题中的string 类型。方便后续比较不同运算符中的优先级 (用swtich语句)
int checkPriority(char ch);
int calculateRPN(vector<char> tokens);

int main()
{
    string s;
    getline(cin,s);
    int ans=calculate(s);
    cout << ans <<endl;
    return 0;
}

int calculate(string s) {
    vector<char>later;
    later=toRPN(s);
    for(int i=0;i<later.size();i++)
    {
        cout << later[i];
    }
    cout <<endl;
        return calculateRPN(later);
}

int checkPriority(char ch)
    {
        switch(ch)
        {
            case '+': return 1;
            case '-': return 1;
            case '*': return 2;
            case '/': return 2;
            default: return 0;
        }
    }

vector<char> toRPN(string s)
    {
        vector<char> s1;   //存储转换后的后缀表达式
        stack<char> op;    //存储等待中的运算符号

        for(int i = 0; i < s.size(); ++i)
        {
            //输入的字符串中可能包含空格 要跳过
            if(s[i] == ' ') continue;

            //如果遇到的是运算符 则需要判断它的优先级 与 当前栈顶的运算符的优先级
            if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
            {
                if(op.empty())
                   op.push(s[i]);
                else
                {
                    if(checkPriority(s[i]) > checkPriority(op.top()))
                        op.push(s[i]);
                    else
                    {
                        while(!op.empty() && (checkPriority(s[i]) <= checkPriority(op.top())))
                        {
                            s1.push_back(op.top());
                            s1.push_back(' ');
                            op.pop();
                        }
                        op.push(s[i]);
                    }
                }
            }
            else if(s[i]>='0' && s[i]<='9')//如果遇到运算数直接放进 s1
            {
              while(s[i]>='0' && s[i]<='9')
              {
                  s1.push_back(s[i]);
                  ++i;
              }
              s1.push_back(' ');
              --i;
            }
        }
        while(!op.empty())
        {
            s1.push_back(op.top());
            s1.push_back(' ');
            op.pop();
        }
        return s1;
    }

int ecalculateRPN(vector<char> tokens)
    {
        stack<int> data;
        int sz = tokens.size();
        for(int i = 0; i < sz; ++i){
            if(tokens[i]=='+'){
                int b = data.top();
                data.pop();
                int a = data.top();
                data.pop();
                data.push(a+b);
            }
            else if(tokens[i]=='-'){
                int b = data.top();
                data.pop();
                int a = data.top();
                data.pop();
                data.push(a-b);
            }
            else if(tokens[i]=='*'){
                int b = data.top();
                data.pop();
                int a = data.top();
                data.pop();
                data.push(a*b);
            }
            else if(tokens[i]=='/'){
                int b = data.top();
                data.pop();
                int a = data.top();
                data.pop();
                data.push(a/b);
            }
            else if(tokens[i]>='0' && tokens[i]<='9')
            {

                int tmp=tokens[i]-'0';
                int j=++i;
                while(tokens[j]>='0' && tokens[j]<='9')
                {
                    tmp=tmp*10+(tokens[j]-'0');//要加括号 不然可能会越界
                    ++j;
                }
                data.push(tmp);
                i=--j;
            }
        }
        return data.top();
    }

在这里插入图片描述

解法:有括号怎么办呢?

其实就是在中缀表达式 转后缀表达式的过程中增加对括号的判定:

  • 左括号:直接放入存储操作数的栈中
  • 右括号:将此时栈顶的操作符弹出并输出到结果数组中,直到遇到左括号(即:右括号不入栈,一直弹出栈顶的运算符,知道遇到左括号,弹出但不输出)
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <cstdlib>
#include <cstring>

using namespace std;

int calculate(string s);
vector<char> toRPN(string s);
int checkPriority(char ch);
int calculateRPN(vector<char> tokens);

int main()
{
    string s;
    getline(cin,s);
    int ans=calculate(s);
    cout << ans <<endl;
    return 0;
}


int calculate(string s) {
    vector<char>later;
    later=toRPN(s);
    for(int i=0;i<later.size();i++)
    {
        cout << later[i];
    }
    cout <<endl;
        return calculateRPN(later);
}


int checkPriority(char ch)
    {
        switch(ch)
        {
            case '+': return 1;
            case '-': return 1;
            case '*': return 2;
            case '/': return 2;
            default: return 0;
        }
    }

vector<char> toRPN(string s)
    {
        vector<char> s1;   //存储转换后的后缀表达式
        stack<char> op;    //存储等待中的运算符号

        for(int i = 0; i < s.size(); ++i)
        {
            //如果遇到空格 跳过
            if(s[i] == ' ') continue;
            if(s[i] == '(')
            {
              op.push(s[i]);
            }
            if(s[i] == ')')
            {
                while(!op.empty() && op.top()!='(')
                {
                    s1.push_back(op.top());
                    s1.push_back(' ');
                    op.pop();
                }
                op.pop();//将栈顶的左括号弹出 
            }

            //如果遇到的是运算符 则需要判断它的优先级 与 当前栈顶的运算符的优先级
            if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
            {
                if(op.empty())
                   op.push(s[i]);
                else
                {
                    if(checkPriority(s[i]) > checkPriority(op.top()))
                        op.push(s[i]);
                    else
                    {
                        while(!op.empty() && (checkPriority(s[i]) <= checkPriority(op.top())))
                        {
                            s1.push_back(op.top());
                            s1.push_back(' ');
                            op.pop();
                        }
                        op.push(s[i]);
                    }
                }
            }
            else if(s[i]>='0' && s[i]<='9')//如果遇到运算数直接放进 s1
            {
              while(s[i]>='0' && s[i]<='9')
              {
                  s1.push_back(s[i]);
                  ++i;
              }
              s1.push_back(' ');
              --i;
            }
        }
        while(!op.empty())
        {
            s1.push_back(op.top());
            s1.push_back(' ');
            op.pop();
        }
        return s1;
    }

int calculateRPN(vector<char> tokens)
    {
        stack<int> data;
        int sz = tokens.size();
        for(int i = 0; i < sz; ++i){
            if(tokens[i]=='+'){
                int b = data.top();
                data.pop();
                int a = data.top();
                data.pop();
                data.push(a+b);
            }
            else if(tokens[i]=='-'){
                int b = data.top();
                data.pop();
                int a = data.top();
                data.pop();
                data.push(a-b);
            }
            else if(tokens[i]=='*'){
                int b = data.top();
                data.pop();
                int a = data.top();
                data.pop();
                data.push(a*b);
            }
            else if(tokens[i]=='/'){
                int b = data.top();
                data.pop();
                int a = data.top();
                data.pop();
                data.push(a/b);
            }
            else if(tokens[i]>='0' && tokens[i]<='9')
            {

                int tmp=tokens[i]-'0';
                int j=++i;
                while(tokens[j]>='0' && tokens[j]<='9')
                {
                    tmp=tmp*10+(tokens[j]-'0');//要加括号 不然可能会越界
                    ++j;
                }
                data.push(tmp);
                i=--j;
            }
        }
        return data.top();
    }

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值