中缀表达式:
在数学学习中,我们常见的就是中缀表达式。
举例:
当我们去掉界限符,上述表达式的运算次数会发生改变:、
因此,在中缀表达式中,界限符是必不可少的,它反映了计算的先后顺序。
前缀表达式和后缀表达式:
在1924年波兰数学家产生了可以不用界限符也能无歧义地表达运算顺序的灵感,由此提出了后缀表达式[逆波兰表达式]和前缀表达式[波兰表达式].
中缀表达式:运算符在两个操作数中间
例如:
a+b
a+b-c
后缀表达式:运算符在两个操作数后面
a b+
a b+c-/a b c-+
前缀表达式:运算符在两个操作数前面
中缀表达式转后缀表达式:
手算方法:
1:确定中缀表达式中各个运算符的运算顺序(运算顺序不唯一,对应的后缀表达式也不唯一)
2:选择下一个运算符,按照[左操作数 右操作数 运算符]的方式组合成一个新的操作数
3:如果还有运算符没被处理,就继续步骤2
举例:
虽然方法一和方法二都是正确的,但是方法一是“机算”。
那么怎么保证手算和机算的结果相同呢?
这里我们介绍一个“左优先”原则:只要左边的运算符能先计算,就优先计算左边的。
举例:
对于上述的中缀表达式,再转化为后缀表达式的过程中,很多人会认为先让CD之间的乘号生效,但实际上A+B之间的加号也可以首先生效,这样一来,还满足了左优先原则。
第二步,肯定就不能计算B-C了,根据先乘除后加减,下面计算C*D
第三步计算D/F
接下来E+F和B-C的优先级相同,但是依然要遵循左优先原则,因此,我们先计算B-C再计算E+F
机算方法:
初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。
从左到右处理各个元素,直到末尾,可能遇到三种情况:
1:遇到操作数,直接加入后缀表达式
2:遇到界限符,遇到“(”直接入栈,遇到“)”,则依次弹出栈内运算符并加入后缀表达式,直到弹出"("为止,注意:“(”不加入后缀表达式。
3:遇到运算符,依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到"("或栈空则停止,之后再把当前运算符入栈。
按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。
第一步:
第二步:
第三步:
第四步:
第五步:
该栈的作用是用于存放当前暂时还不能确定运算次序的运算符。
当表达式中含有界限符:
后缀表达式的计算:
后缀表达式的手算方法:
从左往右扫描,就让运算符前面最近的两个操作数执行对应运算,合体为一个操作数。
注意:两个操作数的左右顺序
后缀表达式的机算方法:
用栈实现后缀表达式的计算:
1:从左往右扫描下一个元素,直到处理完所有元素
2:若扫描到操作数则压入栈,并回到步骤一,否则执行步骤三
3:若扫描到运算符,则弹出两个栈顶元素,执行相应操作,运算结果压回栈顶,回到步骤一
举例:
第一步:
第二步:
第三步:
第三步:
最后一步:因此,如果表达式合法,那么最后栈中只会留下一个元素,也就是该表达式的最终结果。
该栈是是用来存放当前暂时还不能确定运算次序的操作数。
后缀表达式适用于基于栈的编程语言,例如:Forth.PostScript
中缀表达式转前缀表达式:
中缀表达式的手算方法:
1:确定中缀表达式中各个运算符的运算顺序
2:选择下一个运算符,按照[运算符 左操作数 右操作数]的方式组合成一个新的操作数
3:如果还有运算符没被处理,就继续步骤2
与中缀转后缀表达式相类似,如上所示的两种中缀转前缀表达式在客观上都是正确的,但后者是机算。
那么怎么保证手算和机算的结果相同呢?
这里我们介绍一个“右优先”原则:只要右边运算符能先计算,那么就优先计算右边的。
前缀表达式的计算:
用栈实现前缀表达式的计算:
1:从右往左扫描下一个元素,直到处理完所有元素
2:若扫描到操作数则压入栈,并继续执行步骤一,否则执行步骤三
3:若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,继续步骤一
方法和后缀表达式计算基本相同,这里就不过多赘述。
中缀表达式的计算:
用栈实现中缀表达式的计算:
初始化两个栈,操作数栈和运算符栈
若扫描到操作数,则压入操作数栈
若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈(期间也会弹出运算符,每当弹出一个运算符,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,运算结果再压回操作数栈)
举例:
接着扫描到运算符-,由于-和+的优先级相同,因此弹出+,与此同时需要弹出两个操作数,A和B。
接着扫描到/,由于*和/的优先级相同,则弹出 *,同时弹出两个操作数C和D,将/直接压入栈。
接着扫描到加法,依次弹出栈中的/和-.
最后将栈中所有操作数和运算符弹出: