表达式求值

一:前言

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

二:思路及分析

  我们用一个字符数组(即char s[1000])来存储数学表达式,定义一个全局变量g_pos表示s[ ]的下标,下标从0开始。首先我们定义两个栈,optr和opnd,分别存储运算符和运算数,遇到运算数直接放进opnd;遇到运算符,分四种情况:
  (1)遇到负号;
  (2)遇到右括号;
  (3)遇到左括号;
  (4)遇到+-*/;
  前三种情况容易理解,这里就谈下第四种情况。我们都知道四则运算是遵循先乘除再加减的(例如2*3+1),因此对于每个运算符,我们都要和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;  //余数部分  
    int c = 0;
    while (s[pos] >= '0'&&s[pos] <= '9')
    {
        integer *= pow(10, c);
        c++;
        integer += (s[pos] - '0');
        pos++;
    }
    if (s[pos] == '.')
    {
        pos++;
        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()
{

    return 0;
}


转自我的个人博客:http://www.61mon.com/index.php/archives/180/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值