Skip to content

Latest commit

 

History

History
207 lines (176 loc) · 5.58 KB

180-表达式求值.md

File metadata and controls

207 lines (176 loc) · 5.58 KB

一:前言

所谓表达式求值,就是写一个微型计算器。例如输入:(1+9)* 2 / 2 - 1,输出计算结果9。对于这样的问题,我们一般利用栈,模拟数学运算来完成。为了简化问题,在继续下面的分析之前,先在此作个约定:本文只讨论+-*/()基本的四则运算,另外不对意外出现的符号(例如^)和不符合规范的数学表达式(例如2*-1)做异常处理。

二:思路及分析

我们用一个字符数组(即char s[1000])来存储数学表达式,定义一个全局变量g_pos表示s[ ]的下标,下标从0开始。首先我们定义两个栈,optr和opnd,分别存储运算符和运算数,遇到运算数直接放进opnd;遇到运算符,分四种情况: (1):遇到负号; (2):遇到右括号; (3):遇到左括号; (4):遇到+-*/;

前三种情况容易理解,这里就谈下第四种情况。我们都知道四则运算是遵循先乘除再加减的,因此对于每个运算符,我们都要和optr的栈顶符号等级比较,如果这个符号的等级比optr栈顶符号等级高,什么也不做,直接放进栈;如果小于等于,就需要把opnd的栈顶两个数字抽出来,进行计算。这里有个问题,在遇到第一个运算符时,此时optr为空,而我们又需要与optr栈顶运算符比较等级,这时候怎么办?为了解决这个问题,我们在初始化optr的时候,放进一个#,设置其等级为最低。

看到这里你可能还存有一个疑问,如何判定符号-是负号还是减号?例如-1+2 和 5-1+2。分析发现,出现负号只有两种情况: (1):左边是左括号,例如(-1+5*3); (2):字符串的第一个字符,例如-5*6-1。

为了方便代码书写,若出现负号,就在运算数栈加入一个数字0(这也就是下面代码里出现bool值is_minus的原因),即转化一下表达式,例如-1+2转化为0-1+2。

三:代码

/**
 * 
 * author 刘毅(Limer)
 * date   2017-02-24
 * mode   C++ 
 */

#include <iostream>
#include <algorithm>
#include <cstring>
#include <stack>
#include <cmath>

using namespace std;

char s[1000];
int  g_pos;  // 字符数组的下标

/* 字符转数字 */
double Translation(int & pos)
{
    double integer = 0.0;    // 整数部分
    double remainder = 0.0;  // 余数部分

    while (s[pos] >= '0' && s[pos] <= '9')
    {
        integer *= 10;
        integer += (s[pos] - '0');
        pos++;
    }

    if (s[pos] == '.')
    {
        pos++;
        int c = 1;
        while (s[pos] >= '0' && s[pos] <= '9')
        {
            double t = s[pos] - '0';
            t *= pow(0.1, c);
            c++;
            remainder += t;
            pos++;
        }
    }

    return integer + remainder;
}

/* 返回运算符级别 */
int GetLevel(char ch)
{
    switch (ch)
    {
    case '+':
    case '-':
        return 1;
    case '*':
    case '/':
        return 2;
    case '(':
        return 0;
    case '#':
        return -1;
    };
}

/* 对两个数进行运算 */
double Operate(double a1, char op, double a2)
{
    switch (op)
    {
    case '+':
        return a1 + a2;
    case '-':
        return a1 - a2;
    case '*':
        return a1 * a2;
    case '/':
        return a1 / a2;
    };
}

/* 利用两个栈进行模拟计算 */
double Compute()
{
    stack<char> optr;    // 操作符栈
    stack<double> opnd;  // 操作数栈

    optr.push('#');
    int len = strlen(s);
    bool is_minus = true;  // 判断'-'是减号还是负号

    for (g_pos = 0; g_pos < len;)
    {
        //1. 负号  
        if (s[g_pos] == '-' && is_minus)  // 是负号
        {
            opnd.push(0);
            optr.push('-');
            g_pos++;
        }
        //2. 是右括号 ) 
        else if (s[g_pos] == ')')
        {
            is_minus = false;
            g_pos++;

            while (optr.top() != '(')
            {
                double a2 = opnd.top();
                opnd.pop();
                double a1 = opnd.top();
                opnd.pop();
                char op = optr.top();
                optr.pop();

                double result = Operate(a1, op, a2);
                opnd.push(result);
            }

            optr.pop();  // 删除'('  
        }
        //3. 数字  
        else if (s[g_pos] >= '0' && s[g_pos] <= '9')
        {
            is_minus = false;
            opnd.push(Translation(g_pos));
        }
        //4. ( 左括号  
        else if (s[g_pos] == '(')
        {
            is_minus = true;
            optr.push(s[g_pos]);
            g_pos++;
        }
        //5. + - * / 四种  
        else
        {
            while (GetLevel(s[g_pos]) <= GetLevel(optr.top()))
            {
                double a2 = opnd.top();
                opnd.pop();
                double a1 = opnd.top();
                opnd.pop();
                char op = optr.top();
                optr.pop();

                double result = Operate(a1, op, a2);
                opnd.push(result);
            }

            optr.push(s[g_pos]);
            g_pos++;
        }
    }

    while (optr.top() != '#')
    {
        double a2 = opnd.top();
        opnd.pop();
        double a1 = opnd.top();
        opnd.pop();
        char op = optr.top();
        optr.pop();

        double result = Operate(a1, op, a2);
        opnd.push(result);
    }

    return opnd.top();
}

int main()
{
    while (cin >> s)
        cout << "结果为:" << Compute() << endl << endl;

    return 0;
}

数据测试如下图: