前缀、中缀、后缀表达式

来源:互联网 发布:lte是什么网络 编辑:程序博客网 时间:2024/06/17 18:39

算是复习吧=。=

先是看了这个博客。博客给出了中缀转成前缀、后缀表达式的方法(大概和教科书一样)。看了两遍,还是不能完全记住两种表达式的转换流程……emmmmmm,想想怎么理解这两种转换方法。

使用上述博客中的例子,要计算中缀表达式的值,先将其转换为前缀或者后缀表达式:

  • 中缀:(3 + 4) × 5 - 6
  • 前缀:- × + 3 4 5 6
  • 后缀:3 4 + 5 × 6 -

观察三种表达式, 前、后缀表达式和中缀表达式中的数字顺序不变(所以记得最后要调整回原来的顺序),前后缀是运算符相对于数字而言的。

计算表达式(前、后缀)时,遍历表达式,遇到操作数则压栈(操作数栈),遇到运算符则从栈顶弹出两个操作数,计算后压栈。继续遍历,直到结束。(1. 遍历:从操作数开始遍历。对于前缀表达式,操作数在后,从末尾往前遍历;对于后缀表达式,操作数在前,从前往后遍历。 2. 计算:操作数计算的顺序和表达式一致。所以对于前缀表达式,栈顶的操作数在表达式中较前的位置,计算时栈顶op次顶;对于后缀表达式,栈顶的操作数在表达式中较后的位置,计算时次顶op栈顶。)

知道计算的方法之后,回到怎么转换的问题

操作数顺序不变,所以转换过程中,遇到操作数直接压栈。

需要两个栈,一个保存运算符S1,两一个保存中间结果S2.

关于转换的起始位置,前缀是从最后一个开始,后缀是从第一个开始。

中缀-前缀表达式转换

如何确定转换顺序?回到表达式的计算方式,两种表达式计算时进行的每一步运算都是相同的!!!决定计算顺序的是运算符的位置。对于前缀表达式,从末尾开始遍历,遇到数字则压栈,遇到一个运算符将栈顶和次顶取出计算。在中缀-前缀转换时,操作数的入栈S2顺序,应该和计算时一致,即从中缀表达式末尾开始

关于运算符,先不考虑括号。优先级高的先计算,怎么体现先计算呢?还是回到前缀表达式的计算,一旦遇到运算符就进行计算,所以优先级高的运算符先压入S2;优先级相同,理论上计算顺序不重要。

什么时候将运算符压入S2?不考虑括号,从右往左遍历,遇到操作数直接压入S2,遇到第一个运算符时,S2中只有一个操作数,此时不应将运算符压入S2,先把操作符压入S1保存。压入下一个操作数。

对于第二个运算符,有三种情况:

  1. 第一个优先级相同,则可以先将之前的两个操作数用第一个运算符进行计算,将第一个运算符从S1弹出,压入S2;
  2. 优先级高于第一个运算符,说明前两个操作数当前不应该计算,将当前运算符压入S1;
  3. 优先级低于第一个运算符,之前比当前优先级高的都可以从S1弹出,压入S2,再将当前运算符压入S1.

更一般的说,就是,将S1中优先级大于等于自身的运算符弹出,压入到S2,将当前运算符压入S1,最后将S1中剩余运算符弹出,压入S2。(注意:博客中给出的算法是将S1中优先级大于自身的部分弹出到S2中,与弹出优先级大等于自身相比,式子的结果不变,但前者可以保证计算顺序是从左到右计算(猜测,待验证))

特别的,对于括号,括号内的运算应该先完成。怎样先完成运算?将运算符压入S2,即可认为当前运算符负责的运算完成。所以出现左括号时,将S1中最后一个右括号之后的运算符都弹出并压入S2,丢弃右括号。前缀表达式从后开始遍历,先遇到右括号,直接压入S1;遇到左括号时,将S1中的运算符弹出,压入到S2,直到右括号结束。

括号的优先级?新压入运算符发现S1栈顶为右括号,应该压入S1,可以认为右括号优先级比所有运算符低。当右括号不处于栈顶,且右括号之后的所有运算符优先级都高于当前运算符时,弹出S1到右括号的下一个。

最后是博客中提到的转换方法:

  1. 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
  2. 从右至左扫描中缀表达式;
  3. 遇到操作数时,将其压入S2;
  4. 遇到运算符时,比较其与S1栈顶运算符的优先级:
    4.1 如果S1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈;
    4.2 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入S1;
    4.3 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
  5. 遇到括号时:
    5.1 如果是右括号“)”,则直接压入S1;
    5.2 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃;
  6. 重复步骤(2)至(5),直到表达式的最左边;
  7. 将S1中剩余的运算符依次弹出并压入S2;
  8. 依次弹出S2中的元素并输出,结果即为中缀表达式对应的前缀表达式。

中缀-后缀表达式转换

与中缀-前缀表达式转换类似的思路,这里仅列出转化流程:

  1. 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
  2. 从左至右扫描中缀表达式;
  3. 遇到操作数时,将其压入S2;
  4. 遇到运算符时,比较其与S1栈顶运算符的优先级:
    4.1 如果S1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
    4.2 否则,若优先级比栈顶运算符的高,也将运算符压入S1(注意转换为前缀表达式时是优先级较高或相同,而这里则不包括相同的情况);
    4.3 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
  5. 遇到括号时:
    5.1 如果是左括号“(”,则直接压入S1;
    5.2 如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃;
  6. 重复步骤(2)至(5),直到表达式的最右边;
  7. 将S1中剩余的运算符依次弹出并压入S2;
  8. 依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式(转换为前缀表达式时不用逆序)。

对于第4步,在新运算符与S1中的运算符比较优先级时,弹出优先级大等于新运算符的符号,所以有点想