相信参加过笔试面试同学应当见到过表达式求值这道题,下面列举的一道经典的考题,本文将同大家一起细细探讨一下表达式求值这一类问题的求法,希望抛砖引玉,其中有不妥的地方也请大家多多批评指正。
/* 功能:四则运算
* 输入:strExpression:字符串格式的算术表达式,如: "3+2*{1+2*[-4/(8-6)+7]}" * 返回:算术表达式的计算结果 */ public static int calculate(String strExpression) { /* 请实现*/ return 0; }
约束:
pucExpression字符串中的有效字符包括[‘0’-‘9’],‘+’,‘-’, ‘*’,‘/’ ,‘(’, ‘)’,‘[’,
‘]’,‘{’ ,‘}’。pucExpression算术表达式的有效性由调用者保证;
预备知识
- 中缀表达式
我们常见的算术表达式,3*3,它的特点是运算符在数的中间,我们称这类算术表达式为中缀表达式。中缀表达式很符合我们人类的思维习惯,但对于机器运算来讲,却不是太明朗。这是因为算术运算是有运算优先级的,先乘除,后加减;先括号内的,在括号外;相同优先级下从左到右。
5+6/2-34
它的正确理解应该是: 5 + 6/2-34=5+3-34=8-34=8-12=-4
- 后缀表达式
后缀表达式就是运算符在运算数的而后面。它有什么特点吗?我们先看个例子,还是以1)为例,将它转化为后缀表达式形式
562/+34*-
怎么理解呢? 它的解读: 从左到右依次读取,遇到数字放起来,遇到运算符就从数字里面提取两个进行运算,比如例子里面的,62/就是6/2算完后更新到数据结构立面这样数据就为53,这样继续读取+,再从数据里面取出两个运算,这样继续下去,最后数据结构里面剩余的就是最后的运算结果。 很明显这里面用到的数据结构,具有后进先出的特性,栈就有这个性质。
解法
熟悉了上面提到的两个概念后,对题目我们应该有了一个大概的解决策略了,但先别急于写代码,我们再来看看题目。
- 通过上一节分析,我们知道解决这道题可以分两步先变中缀表达式为后缀表达式再对后缀表达式求值
- 题目里面除了±*/四则运算外,还有三种不同类型的括号,别害怕,无论再多的括号类型,我们都明白一个点,他们有一个共同的特性,括号内的表达式要优先运算。可以这样理解为如果把左括号入栈的话,读到对应的右括号,那么需要将栈内左括号之后入栈的运算符全抛出来进行运算。
- 注意到运算数是有负数的,怎么判断?
先看看怎么从字符流里面把运算数给提取出来吧,顺序读取字符,直到非数字,然后将这几个字符组成的字符串转成数字。怎么判断是负数?我的方法是增加一个变量存储上次读到的字符,然后判断前一个不是数字的话,就认为这个符号-是标识负数的意思。
到此我们整个的解题过程就比较明朗了,下面将给出完整的实现代码,注意具体实现的时候并没有讲过程分为明显的两步,而是用两个栈来代替两步过程,实现一遍读取一遍运算。有喜欢的话,可以自行拆分成两步分,这样可能更好理解。
#include <iostream>
#include <stack>
using namespace std;
bool compare(char v1, char v2)
{
if( v2 == '(' || v2 == '[' || v2 == '{') return true;
if( (v1 == '*' || v1 == '/' ) && (v2 == '+' || v2 == '-'))
return true;
return false;
}
void caling(char c, stack<int>& q)
{
int v1 = q.top();
q.pop();
int v2