表达式求值
栈是计算机中的一个常用结构,满足LIFO特性(last in and first out,我们常用子弹夹做例子。)
回到本问题
我们想计算一个表达式的值
例如5+3 * 7 * 8 * (3+5*8) = ?
计算机不像人那么聪明,他不知道运算的顺序
所以问题的核心在于我们先进行哪个运算
我们可以用一个数据栈 一个运算符栈来分别存储数字和运算符,在本题中他把运算符分为了+,-,*,/, ( , ),#
现在让我们来开始分析这个问题吧
究竟进不进行当前运算符的计算由下一个运算符决定且只由下一个运算符决定决定,例如5+3 * 3计算不计算5+3由后面的乘号决定
所以我们需要考虑的就是一个优先级的问题
1.在这里我们这样考虑计算 ,若新来的运算符优先级比栈顶运算符优先级高,则先将此运算符压入栈中;
2.若新来运算符优先级比栈顶低,则弹出符号栈顶元素,同时弹出数据栈中两个元素,将计算结果压入数据栈,新来的那个符号继续与此时栈顶元素比较(若比栈顶元素高则回到第一条,反之回到本条)
3.若两个运算符优先级相同,则两个运算符抵消,即弹出栈顶元素,且开始扫描新来运算符后面的运算
开始讨论吧
#号优先级是最低的
1.数据栈将5与3压入栈中,将+号运算符压入运算符栈中,究竟该不该计算5+3呢,该不该计算5+3由后面的运算符决定,即号决定,而这里显然我们可以得出结论我们不能先计算5+3,所以我们将 * 号压入运算符栈中,同时将7压入数据栈中,==即 / 优先级大于+ -==
此时运算符栈中有# + 和 * 。数据栈有5 3 7
2.当号压入栈中时究竟该不该计算3 * 7呢,正如前文所述 要看后一个运算符在这里我们时 号,需要注意的是,3*7可以计算了于时 后面的乘除优先级小于前边的乘除同理后面的加减小于前面的加减。想想为什么呢
运算符栈弹出* 号,数据栈弹出3 和 7将结果21压入栈中
此时数据符栈中有5和21 运算符栈有# +号
3.前一步中的7 * 8中的乘号该何去何从呢,他就需要和运算符栈顶元素 +号比较了前面第一步已经说了号大于+号,但可不可以计算呢,还要看 * 号后面的运算符,于时我们将 号压入符号栈
此时数据栈有 5 21 8 运算符栈有# +和 *
4.现在到了 * 号前面已经说过了 后面* *号优先级高于前面
所以弹出 * 号 和21 8 将168压入栈中
此时数据栈变为 5 168 运算符栈有 # + *
5.现在轮到(号了(号是一个比较特殊的符号,为什么这么说呢,从数学 来考虑吧,一个很朴素的结论是我们要先计算()里的数字,所以我们可以得出结论(大于+ - * /吗?显然是不可以这样的,假如这样的本题中(的下一个运算符是+号,假如理所当然的吧(运算符优先级看为比+高的话,那么到了下一步(就该弹出栈进行计算了。
所以我们应该这样考虑当(没进栈的话他的优先级最高,当他进了栈之后他的优先级最低
这样考虑就没有问题了
此时运算符栈中有# + * (,数据栈有5 168 3
6.然后轮到了加号,前面已经说过了(进栈之后优先级会变为最低的
所以此时运算符栈中有# + * ( + 数据栈有5 168 3 5
7.下一个运算符是乘号 号该不该计算还得看下一个运算符,将 * 号呀入栈中
此时运算符栈变为# + * ( + * 数据栈有 5 168 3 5 8
8.下一个运算符是),我们把)的优先级看为最低和进栈后的(一样
所以弹出*号 弹出 5 8
数据栈变为 5 168 3 40 符号栈变为 #+ * ( +
9.上一步中的)继续和栈顶加号比较
栈顶加号弹出
数据栈变为 5 168 43 符号栈变为# + * (
10.)继续与栈顶(比较两者优先级相同,两者 抵消即弹出( 同时扫描)后面的元素
数据栈变为 5 168 43 符号栈变为# + *
11.#号与栈顶元素 *比较,#号运算符优先级最低
数据栈变为 5 168 * 43 符号栈变为 # +
12.#号与加号比
数据栈变为5+168*143 符号栈变为#号
13#号与栈顶#号相同 ,相抵消
符号栈变为空数据栈变为5+168*143
运算符优先级规律总结如下
* /大于+ -
后面* /小于前面乘除
后面+ - 小于前面+ -
(没有进栈时候高于所有运算符,进栈之后低于+ - * /
)和进栈之后的(优先级一样,低于+ - * /
#号优先级最低
int Comp(char str1, char str2)
{ /*比较str1和str2的优先级,1:str1高;0:相同;-1:str1低*/
//在这里str1 是没进栈的元素,str2 是栈顶的元素 orz
switch (str1)
{
case '+':
case '-': if (str2 == '(' || str2 == '#') return 1; else return -1; break;
case '*':
case '/': if (str2 == '*' || str2 == '/') return -1; else return 1; break;
case '(': return 1; break;
case ')': if (str2 == '(') return 0; else return -1; break;
case '#': if (str2 == '#') return 0; else return -1; break;
default: break;
}
}