后缀表达式计算四则运算

我们平时书写的算术表达式都是中序表达式,例如

  • (1 + 2) x 3/4 - 5

而对应的后缀表达式就是

  • 12+3x4/5-

如何由中序表达式变为后缀表达式呢?具体做法如下:

  • 建立符号栈s
  • 从左向右扫描中序表达式
    • 碰到数字,直接输出
    • 碰到运算符
      • 如果s为空或者栈顶元素为左括号,则压入s,回到从左向右扫描中序表达式
      • 运算符的优先级大于s栈顶运算符的优先级,压入s,回到从左向右扫描中序表达式
      • 运算符的优先级小于或者等于s栈顶运算符的优先级,弹出并输出s栈顶元素,回到碰到运算符
    • 如果是左括号,压入s
    • 如果是右括号
      • 弹出s栈顶元素
        • 如果弹出的栈顶元素是左括号,则不输出,回到从左向右扫描中序表达式
        • 如果弹出的栈顶元素不是左括号,则输出,回到弹出s栈顶元素
  • 弹出并输出s中的符号

所以对于 (1+2)×3/45 可以这样去做

  • 建立符号栈s

    输出:
    s:
  • 从左向右扫描,第一个左括号,压入s

    输出:
    s:(
  • 1,输出

    输出:1
    s:(
  • +,栈顶元素为左括号,压入s

    输出:1
    s:(+
  • 2,输出

    输出:12
    s:(+
  • 右括号,弹出栈顶元素+并输出

    输出:12+
    s:(

    栈顶元素是左括号

    输出:12+
    s:
  • x,压入s

    输出:12+
    s:x
  • 3,输出

    输出:12+3
    s:x
  • /,优先级等于栈顶元素优先级,弹出并输出栈顶元素

    输出:12+3x
    s:

    栈为空,压入s

    输出:12+3x
    s:/
  • 4,输出

    输出:12+3x4
    s:/
  • -,优先级小于栈顶元素优先级,弹出并输出栈顶元素

    输出:12+3x4/
    s:

    栈为空,压入s

    输出:12+3x4/
    s:-
  • 5,输出

    输出:12+3x4/5
    s:-
  • 弹出并输出s

    输出:12+3x4/5-
    s:

关于后缀表达式的计算,就是所有的计算按运算符出现的顺序,严格从左向右进行即可。但是我们是不是每次都要先将中缀转化为后缀在计算呢?如何按后缀表达式方式的思想计算中序表达式呢?具体做法如下:

  • 建立数字栈s1,符号栈s2
  • 表达式不为空,从左向右扫描中序表达式
    • 碰到数字压入s1
    • 碰到操作符
      • 如果s2为空或栈顶元素为左括号,则压入s2,回到从左向右扫描中序表达式
      • 如果s2不空,且运算符的优先级和s2栈顶的运算符优先级高,则将这个运算符压入s2 ,回到从左向右扫描中序表达式
      • 如果s2不空,且运算符的优先级比s2栈顶的运算符优先级相同或比其低,则从s1 中弹出两个数字与s1栈顶弹出的运算符做运算,将结果压入s1,然后回到碰到操作符。
    • 碰到左括号,则压入s2
    • 碰到右括号
      • 如果s2栈顶元素不是左括号,则从s1 中弹出两个数字与s1栈顶的运算符做运算,将结果压入s1
      • 如果s2栈顶元素是左括号,弹出左括号
  • 表达式为空,如果s2不为空,则从s1 中弹出两个数字与s2栈顶弹出的运算符做运算,将结果压入s1,直到s2为空
  • 如果s1只有一个,ok,否则肯定就出错了^_^!

对于 (1+2)×3/45 这样计算

  • 建立s1,s2

    s1:
    s2:
  • 左括号,压入s2

    s1:
    s2:(
  • 1,压入s1

    s1:1
    s2:(
  • +,s2为空,压入s2

    s1:1
    s2:(+
  • 2,压入s1

    s1:12
    s2:(+
  • 右括号,s2栈顶元素不是左括号,则从s1 中弹出两个数字与s1栈顶的运算符做运算,将结果压入s1

    s1:3
    s2:(

    s2栈顶元素是左括号,弹出左括号

    s1:3
    s2:
  • x,s2为空,压入s2

    s1:3
    s2:x
  • 3,压入s1

    s1:33
    s2:x
  • /,s2不空,且运算符的优先级和s2栈顶的运算符优先级相同,则将这个运算符压入s2

    s1:33
    s2:x/
  • 4,压入s1

    s1:334
    s2:x/
  • -,s2不空,且运算符的优先级比s2栈顶的运算符优先级相同或比其低,则从s1 中弹出两个数字与s1栈顶弹出的运算符做运算,将结果压入s1

    s1:3 0.75
    s2:x

    s2不空,且运算符的优先级比s2栈顶的运算符优先级相同或比其低,则从s1 中弹出两个数字与s1栈顶弹出的运算符做运算,将结果压入s1

    s1:2.25
    s2:

    s2为空,压入s2

    s1:2.25
    s2:-
  • 5,压入s1

    s1:2.25 5
    s2:-
  • s2不为空,则从s1 中弹出两个数字与s2栈顶弹出的运算符做运算,将结果压入s1

    s1:-2.75
    s2:

最后一个问题就是关于负数和浮点数的问题,关于这方面的处理我放在了代码中。负数的问题,主要分为减号语意和负数语意去处理。我觉得这里最简单的方式,就是对于负号前面空或者是左括号,我们给它插上个0,这样问题就归为了减号语意一个问题。当然分两个去做也没有问题,只是讨论情况会很麻烦。

/**********************************************************
*2017_10_2 by coordinte
*前缀四则运算
*win10 vs2015 x86 debug 
*www.coordinate.wang
************************************************************/
#include <iostream>
#include <stack>        //内部是通过deque实现的
#include <string>
#include <map>
#include <cctype>       //isdigit
#include <cstdlib>      //stod
#include <stdexcept>
#include <algorithm>    //remove
using std::string;

//运算符的优先级
std::map<char, int> priority{ { '+', 1 },
                            { '-', 1 },
                            { '*', 2 },
                            { '/', 2 } };   

//计算两个数的值,返回结果
double execute(std::stack<char>& ops, std::stack<double>& operands)
{
    double result{};
    double rhs{ operands.top() }; operands.pop();
    double lhs{ operands.top() }; operands.pop();
    switch (ops.top())
    {
    case '+':
        result = lhs + rhs;
        break;
    case '-':
        result = lhs - rhs;
        break;
    case '*':
        result = lhs * rhs;
        break;
    case '/':
        result = lhs / rhs;
        break;
    default:
        throw std::runtime_error{ string{ "invalid operator: " + ops.top() } };
    }
    ops.pop();
    operands.push(result);
    return result;
}

//将输入字符串转化为前缀形式
double toSuffix(string str_in)
{
    std::stack<char> operators;
    std::stack<double> operands;
    size_t index{};     //标记字符串中数字的下一个字符的位置
    size_t i{};         //标记字符的位置www.coordinate.wang
    while (i < str_in.length())
    {
        char s_i = str_in[i];
        if (s_i == '(')
        {
            operators.push(s_i);
            ++i;
        }   
        else if (s_i == ')')
        {
            char temp{};
            while ((temp = operators.top()) != '(')
            {
                execute(operators, operands);
                if (operators.empty())
                {
                    return -1;
                }
            }
            //去除temp
            operators.pop();
            ++i;
        }
        //处理操作符
        else if (priority.count(s_i))           
        {
            while (!operators.empty() && operators.top() != '('
                && priority.at(s_i) <= priority.at(operators.top()))
            {
                execute(operators, operands);
            }
            operators.push(s_i);
            ++i;
        }   
        else//处理数字
        {
            double number = std::stod(str_in.substr(i), &index);
            operands.push(number);
            i += index;
        }
    }
    while (!operators.empty())
    {
        execute(operators, operands);
    }
    double result = operands.top(); operands.pop();
    if (!operands.empty())
        return -1;
    return result;
}

//输入字符串的小数点前要有数字,如0.1,不可以是.1
int main()
{
    string str_in;
    std::cout << "An arithmetic can include the operators +,-,*,/." << std::endl;
    try
    {
        while (true)
        {
            std::cout << "Enter an arithmetic expression and press Enter"
                << " - enter an empty line to end:"
                << std::endl;
            std::getline(std::cin, str_in, '\n');
            if (str_in.empty()) break;

            //删除空格
            str_in.erase(std::remove(std::begin(str_in), std::end(str_in), ' '), std::end(str_in));
            //如果负号前面是空或者左括号的话插入0
            for (size_t i = 0; i < str_in.length(); ++i)
            {
                if (str_in[i] == '-' && i == 0)
                {
                    str_in.insert(0, 1, '0');
                }
                else if (str_in[i] == '-' && str_in[i - 1] == '(')
                {
                    str_in.insert(i, 1, '0');
                }
            }
            std::cout << toSuffix(str_in) << std::endl;
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
    std::cout << "Calculator ending ..." << std::endl;
    system("pause");
}

这个程序稍加改动就可以输出后缀表达式了。不再赘述,留给读者思考。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值