前缀、中缀、后缀表达式之间的相互转换及部分实现

背景介绍

前缀表达式(又称为波兰表达式)
中缀表达式
后缀表达式(又称为逆波兰表达式)

平时我们见到的都是:2+3*5,这样的算数表达式(中缀),我们数学中也就是用这种
这种表达式(中缀)只适合人来读写,不适合计算机,所以为了方便计算机读取正确的表达式就有了前缀后缀表达式

本文将会介绍 :

前缀转中缀,中缀转前缀
中缀转后缀,后缀转中缀
前后缀之间的相互转换可以用中缀作为中间商来解决,或者也可以构造语法树遍历来解决

前缀转后缀的具体实现代码(构造树实现)
https://www.cnblogs.com/breakthings/p/4053444.html


例子:2+(3-1)*4(中缀,语法树中序遍历可以得到)
231-4 * + (后缀,语法树后序遍历可以得到,其他方法得到也行,表达式不唯一)
+2*-314 (前缀,语法树先序遍历可以得到)

为了方便理解,我画了表达式的 语法树
叶子节点都是,非叶子节点都是运算符。画图很简单就不赘述了

前缀、中缀、后缀表达式之间的相互转换及部分实现_第1张图片

1. 中缀转前缀

如果仅仅是写题目,仅需把语法树用前序遍历(又称为先序遍历)一遍即可得到答案 +2*-314(前缀表达式)

但一般实现是使用

思路:

  1. 首先要处理字符串,运算符的优先级,多位数字看成整体(比如23看成一个整体而不是2和3),我的例子中没那么复杂
  2. 创建两个栈,一个是数字栈(里面存的是最终结果),一个是运算符栈
  3. 从右往左 扫描中缀表达式
  4. 若是运算符,则与运算符栈顶元素比较优先级:
    若该运算符优先级大于等于栈顶元素,则将该运算符入栈;
    若该运算符优先级小于栈顶元素,则运算符栈内元素出栈并压入数字栈,再与其比较,直到该运算符优先级大于等于栈顶元素的优先级时,将该运算符压入栈中。
  5. 注意:我们先在运算符栈中压入一个‘#’ 规定其优先级为最低 这样解决了开始时候的边界问题)
    注意:遇到右括号直接压入栈中,遇到一个左括号,那么就将运算符栈元素弹出并压入数字栈直到遇到右括号,并且把右括号弹出,停止弹出。这个过程,两个括号都不加入数字栈。
  6. 表达式遍历完后,若运算符栈还有元素,则依次弹出压入数字栈。
  7. 把数字栈元素从栈顶依次弹出,就得到前缀表达式。

代码实现:

例:1-(2+3) 转换前缀是:- 1 + 2 3
例:1+((2+3)*4)-5 转换前缀是:- + 1 * + 2 3 4 5
例:123+((246+39)*48)-55 转换前缀是:- + 123 * + 246 39 48 55

参考博客:https://blog.csdn.net/holly_Z_P_F/article/details/98724274

#include
#include
#include
#include
using namespace std;
const int maxn=1005;
typedef struct NODE{
    string ch;
    int level;
}NODE;
NODE node[maxn];
string s;
int change(){
    int w1=0;
    for(int i=0;i<s.size();i++){  //划分优先级
        if(s[i]=='*'||s[i]=='/'){
            node[w1].ch=s[i];
            node[w1++].level=2;
        }
        else if(s[i]=='+'||s[i]=='-'){
            node[w1].ch=s[i];
            node[w1++].level=1;
        }
        else if(s[i]=='('||s[i]==')'){
            node[w1].ch=s[i];
            node[w1++].level=0;
        }
        else if(s[i]>='0'&&s[i]<='9'){ //分割字符串
            int j=i;
            while(s[j]>='0'&&s[j]<='9'){ j++;}
            node[w1].ch=s.substr(i,j-i);
            node[w1].level=100;
            w1++;
            i=j-1;
        }
    }
    return w1;
}
void solove(int l){
    NODE ans[maxn]; //结果栈
    NODE q[maxn];  //运算符栈
    int w2=0,w3=0;
    q[w3].ch="#";  q[w3++].level=-1;//先压入 #
    for(int i=l-1;i>=0;i--){
        if(node[i].level==100){
            ans[w2++]=node[i];
        }
        else{
            if(node[i].ch==")") q[w3++]=node[i];
            else if(node[i].ch=="("){
                while(q[w3-1].ch!=")"){
                    w3--; ans[w2++]=q[w3];//压入结果栈
                }
                w3--;
            }
            else{
                while(node[i].level<q[w3-1].level){ //比较优先级
                    w3--; ans[w2++]=q[w3];
                }
                q[w3++]=node[i];
            }
        }
    }
    while(w3!=1){
        w3--;  ans[w2++]=q[w3];
    }
    for(int i=w2-1;i>=0;i--){//倒序输出
        cout<<ans[i].ch<<" ";
    }
    return ;
}
int main(){
    cin>>s;        //输入表达式
    int l=change();//处理表达式
    solove(l);     //转换为前缀表达式
    return 0;
}

2. 前缀转中缀

(这一点网上的资料很少)
例子:+2*-314(前缀)-----> 2+((3-1)*4)(中缀)

思路:

准备一个栈

  1. 从右往左扫描前缀表达式
  2. 遇见数字压入数字栈
  3. 遇见运算符栈,则弹出数字栈的两个元素,并且运算,前后还要添加括号,然后压入栈
    例如:栈中有2和3(2个元素),现在遇见+了,则把 (2+3) 压入栈,栈中现在就有5个元素了,后面的依次类推
    结果中肯定会出现很多多余的括号,也可以解决但是比较复杂就不详细说了

代码实现:

实现起来较为复杂,有机会补上…

3. 中缀转后缀

写答案的话后序遍历的语法树,可以直接得到答案

思路:

和中缀转前缀差不多,只是方向改变了

  1. 从左往右扫描中缀表达式
  2. 其他的与中缀转前缀的操作一样

这里有一篇图解博客,方便大家理解
https://www.cnblogs.com/lanhaicode/p/10776166.html

4. 后缀转中缀

思路:

与前缀转中缀的思路一样

  1. 从右往左扫描后缀表达式
  2. 遇见数字压入数字栈
  3. 遇见运算符栈,则弹出数字栈的两个元素,并且运算,前后还要添加括号,然后压入栈
    例如:栈中有2和3(2个元素),现在遇见+了,则把 (2+3) 压入栈,栈中现在就有5个元素了,后面的依次类推

1 2 3 + 4 *5 - +

例如:
从左往右扫描先碰到+号,取+号前面两个操作数:2,3 得到:(2+3).
继续往下扫碰到*号,取4 和2+3 得到:((2+3)*4)
-号,取(2+3)*4和5得到::(((2+3)*4)-5))
+号:取(2+3)*4-5和1得到::1+(((2+3)*4)-5))
如果把多余的括号省略就是1+(2+3)*4-5


如有错误,不吝赐教

你可能感兴趣的:(数据结构)