中缀/后缀/前缀表达式及相互转换的手算详细步骤及C代码实现


1 三种算术表达式

算术表达式由三个部分组成:操作数、运算符、界限符。界限符是必不可少的,也就是括号。括号或者说界限符反映了计算或者说运算符作用的先后顺序。但是有一个波兰数学家想这样做:可以不用界限符也能无歧义地表达运算顺序。于是发明了:① 逆波兰表达式,即后缀表达式;② 波兰表达式,即前缀表达式。

类型规则例1例2
中缀表达式运算符在两个操作数中间a+ba+b-c
后缀表达式运算符在两个操作数后面ab+ab+c-
前缀表达式运算符在两个操作数前面+ab-+abc

2 后缀表达式相关考点

2.1 中缀表达式转后缀表达式

2.1.1 手算

中缀转后缀的手算步骤:

① 确定中缀表达式中各个运算符的运算顺序,但是有时候运算顺序不唯一,因此对应的后缀表达式也不唯一。为了保证手算和机算结果相同,且保证运算顺序唯一,请遵从“左优先”原则:只要左边的运算符能先计算,就优先算左边的。确定完运算符的运算顺序后,如果有界限符即括号,就可以去掉全部的括号了,或者说可以忽略括号的存在继续下面步骤;

② 选择下一个运算符,按照【左操作数 右操作数 运算符】的方式组合成一个新的式子,然后放在原来操作符的位置。如果还有运算符没被处理,就继续②,否则最后得到的式子就是最终结果。下面是一个示例:

转换前的中缀表达式转换后的后缀表达式
((15/(7-(1+1)))*3)-(2+(1+1))15 7 1 1 + - / 3 * 2 1 1 + + -
2+(3+4)*52 3 4 + 5 * +
16+2*30/416 2 30 * 4 / +

2.1.2 机算

实现中缀表达式转后缀表达式的逻辑过程:初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。从左到右扫描中缀表达式的各个字符,直到末尾。在扫描时可能遇到三种情况:

① 当前字符是数字:直接加入后缀表达式,然后处理下一个字符;

② 当前字符是括号,又分为两种情况:

  • 1)当前字符是左括号即(则直接将当前字符入栈,然后处理下一个字符;
  • 2)当前字符是右括号即):若栈空则直接处理下一个字符。若栈非空则不断弹出栈顶元素——若弹出左括号(后则停止继续弹出(注意这个左括号(不加入后缀表达式)并直接处理下一个字符,否则将栈顶元素加入后缀表达式,然后继续弹出栈顶元素;

③ 当前字符是运算符,又分为两种情况:

  • 1)栈空则直接把当前字符入栈,然后处理下一个字符;
  • 2)栈非空则不断弹出栈顶元素:若栈顶元素是左括号(或优先级低于当前字符的运算符则停止栈顶元素的弹出,再将栈顶元素再次入栈,之后再把当前字符入栈,然后处理下一个字符。若栈顶元素是运算符且优先级高于或等于当前字符,则将栈顶元素加入后缀表达式,然后继续弹出栈顶元素;

按上述方法处理完所有字符后,若栈非空则将栈中剩余字符依次弹出并依次加入后缀表达式。最后得到的表达式就是最终结果。示例代码如下:

//本程序只能处理有关运算符+、-、*、/的中缀表达式,不能是÷或者×及其他运算
//界限符只能是英文状态的左右括号即'('、')',操作数只能是整数
//本程序不会检查输入的中缀表达式是否正确,因此请您核验好自己的式子是否正确
#include<stdio.h>
#include<string.h> //strlen的头文件,用于判断字符串长度
#include<stdlib.h> //malloc、free的头文件
#define size 50//假定要转换的中缀表达式的字符数在50个以内
typedef struct Linknode{ //定义链栈及结点
    char data; //数据域
    struct Linknode *next; //指针域
}*LiStack;
bool InitStack(LiStack &S){ //链栈的初始化,不带头结点
    S=NULL; //刚开始没有结点
    return true;
}
bool StackEmpty(LiStack S){ //判断栈空
    return S==NULL;
}
bool Push(LiStack &S,char x){ //将元素x入栈
    Linknode *s=(Linknode *)malloc(sizeof(Linknode)); //创建新结点
    if(s==NULL) //内存不足,创建失败
        return false;
    s->data=x;
    s->next=S; //将结点s作为链栈的栈顶结点
    S=s; //栈顶指针S指向结点s
    return true;
}
bool Pop(LiStack &S,char &x){ //栈顶元素出栈,将值赋给x
    if(S==NULL)
        return false; //栈空则返回NULL
    x=S->data;
    Linknode *p=S;
    S=S->next;
    free(p);
    return true;
}
int main(){
    char temp,a[size],b[size]; //静态数组a、b分别存放要转换的中缀表达式和转换后的后缀表达式,字符变量temp存放弹出的栈顶元素
    scanf("%s",&a); //需要您输入中缀表达式
    LiStack S;//初始化一个栈,用于保存括号和暂时还不能确定运算顺序的运算符
    InitStack(S); //初始化链栈
    int i,j,length=strlen(a); //length为输入的中缀表达式的总长度,i、j分别为静态数组a、b的索引下标
    for(i=j=0;i<length;i++){
        if(a[i]>=48 && a[i]<=57){ //若当前字符是数字,字符0-9的ACSII码范围是[48,57]
            b[j++]=a[i];
            if(a[i+1]=='+'||a[i+1]=='-'||a[i+1]=='*'||a[i+1]=='/') //若下一个字符是运算符,即+、-、*、/,则b加一个空格,以免不同的操作数混在一起
                b[j++]=' ';
        }
        else if(a[i]=='(')
            Push(S,a[i]); //若当前字符是左括号则直接入栈
        else if(a[i]==')'){ //若当前字符是右括号
            while(StackEmpty(S)==0){ //栈非空则不断弹出栈内字符并加入后缀表达式
                Pop(S,temp);
                if(temp=='(') //直到弹出左括号停止,注意这个(不加入后缀表达式
                    break;
                b[j++]=temp;
                b[j++]=' '; //加一个空格,从而将字符隔开
            }
        }
        else switch(a[i]){ //若当前字符是运算符
            case '*': case '/':{
                while(StackEmpty(S)==0){ //若栈非空,则弹出栈中优先级高于或等于当前运算符的所有运算符,并将这些运算符加入后缀表达式
                    Pop(S,temp);
                    if(temp=='/'||temp=='*'){
                        b[j++]=temp;
                        b[j++]=' '; //加一个空格,从而将字符隔开
                    }
                    else if(temp=='('||temp=='-'||temp=='+'){//若栈顶元素是左括号或者是优先级低于当前字符的运算符,则将栈顶元素入栈
                        Push(S,temp);
                        break;
                    }
                }
                Push(S,a[i]); //把当前字符入栈
                break;
            }
            case '-': case '+':{
                while(StackEmpty(S)==0){ //若栈非空,则弹出栈中优先级高于或等于当前运算符的所有运算符,并将这些运算符加入后缀表达式
                    Pop(S,temp);
                    if(temp=='('){//若栈顶元素是左括号,则将栈顶元素入栈
                        Push(S,temp);
                        break;
                    }
                    else if(temp=='/'||temp=='*'||temp=='-'||temp=='+'){
                        b[j++]=temp;
                        b[j++]=' '; //加一个空格,从而将字符隔开
                    }
                }
                Push(S,a[i]); //把当前字符入栈
                break;
            }
        }
    }
    while(StackEmpty(S)==0){ //栈非空时依次弹出栈顶元素并加入后缀表达式
        Pop(S,temp);
        b[j++]=temp;
        b[j++]=' '; //加一个空格,从而将字符隔开
    }
    printf("结果是:\n");
    for(i=0;i<j;i++) //j是数组中下一个可以插入元素的位置下标,因此b中存放字符的索引区间为[0,j-1]
        printf("%c",b[i]); //输出b中的元素
    printf("\n");
    return 0;
}

运行程序后需要输入待转换的中缀表达式,下面是三个输入输出示例:

输入输出
((15/(7-(1+1)))*3)-(2+(1+1))15 7 1 1 + - / 3 * 2 1 1 + + -
2+(3+4)*52 3 4 + 5 * +
16+2*30/416 2 30 * 4 / +

2.2 后缀表达式求值

2.2.1 手算

后缀表达式求值的手算步骤:从左往右扫描后缀表达式的每一个字符,每遇到一个运算符,就选择运算符左面距离最近的两个操作数执行对应运算,执行运算时注意两个操作数的左右顺序,得到计算结果后去掉刚刚的两个操作数,将新得到的计算结果放在刚刚的这个运算符的位置并代替之,继续从左到右扫描字符直到扫描完全部字符。扫描结束最后得到的就是最终结果。下面是一个示例:

求值前求值后
15 7 1 1 + - / 3 * 2 1 1 + + -5
2 3 4 + 5 * +37
16 2 30 * 4 / +31

2.2.2 机算

需要初始化一个栈,用于存放当前暂时还不能确定运算次序的操作数。用栈实现后缀表达式求值的逻辑过程:

① 从左往右扫描后缀表达式的每一个字符,直到扫描完所有字符;

② 若扫描到的字符是操作数则压入栈,并回到①,否则执行③;

③ 若扫描到的字符是运算符,则连续弹出两个栈顶元素(注意:先出栈的是“右操作数”),然后对这两个操作数执行相应运算,然后再将运算结果入栈,回到①。

若被扫描的后缀表达式是合法的,则最后栈中只会留下一个元素,就是最终结果。示例代码如下:

//本程序只能处理有关运算符+、-、*、/的后缀表达式,不能是÷或者×及其他运算
//本程序不会检查输入的后缀表达式是否正确,因此请您核验好自己的式子是否正确
//操作数长度在10位以内且必须是整数
//请将不同的操作数之间、运算符之间、运算符与操作数之间用英文状态的逗号即","隔开
#include<stdio.h>
#include<math.h> //pow的头文件,用于求乘方数
#include<string.h> //strlen的头文件,用于判断字符串长度
#include<stdlib.h> //malloc、free的头文件
#define size 50//假定要求值的后缀表达式的字符数在50个以内
#define num_length 10 //假定后缀表达式中的每一个操作数最长为10位数
typedef struct Linknode{ //定义链栈及结点
    float data; //数据域
    struct Linknode *next; //指针域
}*LiStack;
bool InitStack(LiStack &S){ //链栈的初始化,不带头结点
    S=NULL; //刚开始没有结点
    return true;
}
bool Push(LiStack &S,float x){ //将元素x入栈
    Linknode *s=(Linknode *)malloc(sizeof(Linknode)); //创建新结点
    if(s==NULL) //内存不足,创建失败
        return false;
    s->data=x;
    s->next=S; //将结点s作为链栈的栈顶结点
    S=s; //栈顶指针S指向结点s
    return true;
}
bool Pop(LiStack &S,float &x){ //栈顶元素出栈,将值赋给x
    if(S==NULL)
        return false; //栈空则返回NULL
    x=S->data;
    Linknode *p=S;
    S=S->next;
    free(p);
    return true;
}
int main(){
    char a[size]; //静态数组a存放要处理的后缀表达式
    float right,left,tmp=0; //right,left为弹出栈的元素,依次为右操作数和左操作数
    int b[num_length];//静态数组b存放操作数的各位数字如231的2、3、1
    scanf("%s",&a); //需要您输入后缀表达式
    LiStack S;//初始化一个栈,用于存放当前暂时还不能确定运算次序的操作数
    InitStack(S); //初始化链栈
    int i,j=0,length=strlen(a); //length为输入的后缀表达式的总长度,i、j分别为静态数组a、b的索引下标
    for(i=0;i<length;i++){
        if(a[i]>=48 && a[i]<=57){ //若当前字符是数字,字符0-9的ACSII码范围是[48,57]
            b[j++]=a[i]-48; //字符型的数字减去48就是数字本身,如ASCII码为51的字符为"3",而"3"-48=51-48=3就是数字3本身
            if(a[i+1]==','||a[i+1]=='+'||a[i+1]=='-'||a[i+1]=='*'||a[i+1]=='/'){ //若下一个字符是逗号或者运算符,则代表凑够一个操作数了
                int m=j;
                for(int k=0;k<m;k++) // 将分散的各位数字组成一个操作数
                    tmp+=b[k]*int(pow(10,--j));
                Push(S,tmp); //将当前操作数入栈
                tmp=0; //在遇到下一个操作数之前将tmp值归零
            }
        }
        else switch(a[i]){ //若当前字符是运算符
            case '*': case '/': case '-': case '+':{
                Pop(S,right); //先出栈的是“右操作数”
                Pop(S,left);  //后出栈的是“左操作数”
                if(a[i]=='+')
                    Push(S,left+right); //执行运算之后将结果入栈,下面三个也是这样
                else if(a[i]=='-')
                    Push(S,left-right);
                else if(a[i]=='*')
                    Push(S,left*right);
                else if(a[i]=='/')
                    Push(S,left/right);
            }
        }
    }
    Pop(S,tmp); //若被扫描的后缀表达式是合法的,则最后栈中只会留下一个元素,就是最终结果
    printf("结果是:%.2f\n",tmp); //输出结果
    return 0;
}

运行程序后需要输入待求值的后缀表达式,注意本程序只能处理有关运算符+、-、*、/的后缀表达式,不能是÷或者×及其他运算,本程序不会检查输入的后缀表达式是否正确,因此请您核验好自己的式子是否正确,操作数长度在10位以内且必须是整数,请将不同的操作数之间、运算符之间、运算符与操作数之间用英文状态的逗号即","隔开。下面是三个输入输出示例:

输入输出
15,7,1,1,+,-,/,3,*,2,1,1,+,+,-5.00
2,3,4,+,5,*,+37.00
16,2,30,*,4,/,+31.00

2.3 后缀表达式转中缀表达式

2.3.1 手算

后缀表达式转换成中缀表达式的手算步骤:从左往右扫描后缀表达式的每一个字符,每遇到一个运算符,就选择运算符左面距离最近的两个操作数,将【左操作数 右操作数 运算符】变为(左操作数 运算符 右操作数)的形式。注意两个操作数的左右顺序,得到一个式子后去掉刚刚的两个操作数,将得到的式子放在刚刚的这个运算符的位置并代替之,继续从左到右扫描字符直到扫描完全部字符。扫描结束最后得到的式子就是最终结果。下面是一个示例:

转换前的后缀表达式转换后的中缀表达式
15 7 1 1 + - / 3 * 2 1 1 + + -(((15/(7-(1+1)))*3)-(2+(1+1)))
2 3 4 + 5 * +(2+((3+4)*5))
16 2 30 * 4 / +(16+((2*30)/4))

2.3.2 机算

需要初始化一个栈,用于存放当前暂时还不能确定运算次序的操作数。用栈实现后缀表达式转中缀表达式的逻辑过程:

① 从左往右扫描下一个元素,直到处理完所有元素;

② 若扫描到操作数则压入栈,并回到①,否则执行③;

③ 若扫描到运算符,则弹出两个栈顶元素(注意:先出栈的是“右操作数”),构成(左操作数 运算符 右操作数),然后将其压回栈顶,回到①。

若被扫描的后缀表达式是合法的,则最后栈中只会留下一个元素,就是最终结果。示例代码如下:

//本程序只能处理有关运算符+、-、*、/的后缀表达式,不能是÷或者×及其他运算
//本程序不会检查输入的后缀表达式是否正确,因此请您核验好自己的式子是否正确
//操作数长度在10位以内且必须是整数
//请将不同的操作数之间、运算符之间、运算符与操作数之间用英文状态的逗号即","隔开
#include<stdio.h>
#include<string.h> //strlen的头文件,用于判断字符串长度
#include<stdlib.h> //malloc、free的头文件
#define size 50//假定输入的后缀表达式的字符数在50以内
#define num_length 10 //假定每一个操作数最长为10位数
typedef struct Linknode{ //定义链栈及结点
    char data[size]; //数据域
    struct Linknode *next; //指针域
}*LiStack;
bool InitStack(LiStack &S){ //链栈的初始化,不带头结点
    S=NULL; //刚开始没有结点
    return true;
}
bool Push(LiStack &S,char *x){ //将元素x入栈
    Linknode *s=(Linknode *)malloc(sizeof(Linknode)); //创建新结点
    if(s==NULL||strlen(x)>size) //内存不足或者长度超过size,则失败
        return false;
    int i;
    for(i=0;i<strlen(x);i++)
        s->data[i]=x[i];
    s->data[i]='\0';
    s->next=S; //将结点s作为栈顶结点
    S=s; //栈顶指针S指向结点s
    return true;
}
bool Pop(LiStack &S,char *x){ //栈顶元素出栈,将值赋给x
    if(S==NULL)
        return false; //栈空则返回NULL
    Linknode *p=S; //p指向栈顶结点
    int i;
    for(i=0;p->data[i]!='\0';i++)
        x[i]=p->data[i];
    x[i]='\0';
    S=p->next;
    free(p);
    return true;
}
int main(){
    char a[size]; //静态数组a存放要处理的后缀表达式
    char right[size],left[size]; //right,left为弹出栈的元素,依次为右操作数和左操作数
    char b[num_length];//静态数组b存放操作数的各位数字如231的'2'、'3'、'1'
    scanf("%s",&a); //需要您输入后缀表达式
    LiStack S;//初始化一个栈,用于存放当前暂时还不能确定运算次序的字符串形式的操作数
    InitStack(S); //初始化链栈
    int i,j=0,length=strlen(a); //length为输入的后缀表达式的总长度,i、j分别为静态数组a、b的索引下标
    for(i=0;i<length;i++){
        if(a[i]>=48&&a[i]<=57){ //若当前字符是数字,字符0-9的ACSII码范围是[48,57]
            b[j++]=a[i];
            if(a[i+1]==','||a[i+1]=='+'||a[i+1]=='-'||a[i+1]=='*'||a[i+1]=='/'){ //若下一个字符是逗号或者运算符,则代表凑够一个操作数了
                b[j]='\0';
                Push(S,b); //将当前操作数入栈
                j=0; //将j归零
            }
        }
        else switch(a[i]){
            case '*': case '/': case '-': case '+':{ //若当前字符是运算符
                Pop(S,right); //先出栈的是“右操作数”
                Pop(S,left);  //后出栈的是“左操作数”
                if(strlen(left)+strlen(right)+1+2>size) // 左右操作数长度之和再加上操作符、左右括号的长度过长,容不下
                    return false;
                char tmp[size]; //临时变量
                tmp[0]='('; //先存左括号
                int k;
                for(k=0;k<strlen(left);k++)
                    tmp[k+1]=left[k]; //存左操作数
                tmp[strlen(left)+1]=a[i]; //存运算符
                for(k=0;k<strlen(right);k++)
                    tmp[k+strlen(left)+2]=right[k]; //存右操作数
                tmp[strlen(left)+strlen(right)+2]=')'; //最后存右括号
                tmp[strlen(left)+strlen(right)+3]='\0';
                Push(S,tmp); //将结果入栈
            }
        }
    }
    char c[size];
    Pop(S,c); //若被扫描的后缀表达式是合法的,则最后栈中只会留下一个元素,就是最终结果
    printf("结果是:%s\n",c); //输出结果
    return 0;
}

运行程序后需要输入待转换的后缀表达式,注意本程序只能处理有关运算符+、-、*、/的后缀表达式,不能是÷或者×及其他运算,本程序不会检查输入的后缀表达式是否正确,因此请您核验好自己的式子是否正确,操作数长度在10位以内且必须是整数,请将不同的操作数之间、运算符之间、运算符与操作数之间用英文状态的逗号即","隔开。下面是三个输入输出示例:

输入输出
15,7,1,1,+,-,/,3,*,2,1,1,+,+,-(((15/(7-(1+1)))*3)-(2+(1+1)))
2,3,4,+,5,*,+(2+((3+4)*5))
16,2,30,*,4,/,+(16+((2*30)/4))

3 前缀表达式相关考点

3.1 中缀表达式转前缀表达式

3.1.1 手算

中缀转前缀的手算步骤:

① 确定中缀表达式中各个运算符的运算顺序,但是有时候运算顺序不唯一,因此对应的前缀表达式也不唯一。为了保证手算和机算结果相同,且保证运算顺序唯一,请遵从“右优先”原则:只要右边的运算符能先计算,就优先算右边的。确定完运算符的运算顺序后,如果有界限符即括号,就可以去掉全部的括号了,或者说可以忽略括号的存在继续下面步骤;

② 选择下一个运算符,按照【运算符 左操作数 右操作数】的方式组合成一个新的式子,然后放在原来操作符的位置。如果还有运算符没被处理,就继续②,否则最后得到的式子就是最终结果。下面是一个示例:

转换前的中缀表达式转换后的前缀表达式
((15/(7-(1+1)))*3)-(2+(1+1))- * / 15 - 7 + 1 1 3 + 2 + 1 1
2+(3+4)*5+ 2 * + 3 4 5
16+2*30/4+ 16 * 2 / 30 4

3.1.2 机算

实现中缀表达式转前缀表达式的逻辑过程:初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。从右到左扫描中缀表达式的各个字符,直到末尾。在扫描时可能遇到三种情况:

① 当前字符是数字:直接加入前缀表达式,然后处理下一个字符;

② 当前字符是括号,又分为两种情况:

  • 1)当前字符是右括号即)则直接将当前字符入栈,然后处理下一个字符;
  • 2)当前字符是左括号即(:若栈空则直接处理下一个字符。若栈非空则不断弹出栈顶元素——若弹出右括号)后则停止继续弹出(注意这个右括号)不加入前缀表达式)并直接处理下一个字符,否则将栈顶元素加入前缀表达式,然后继续弹出栈顶元素;

③ 当前字符是运算符,又分为两种情况:

  • 1)栈空则直接把当前字符入栈,然后处理下一个字符;
  • 2)栈非空则不断弹出栈顶元素:若栈顶元素是右括号)或优先级高于当前字符的运算符则停止栈顶元素的弹出,再将栈顶元素再次入栈,之后再把当前字符入栈,然后处理下一个字符。若栈顶元素是运算符且优先级低于或等于当前字符,则将栈顶元素加入前缀表达式,然后继续弹出栈顶元素;

按上述方法处理完所有字符后,若栈非空则将栈中剩余字符依次弹出并依次加入前缀表达式。将最后得到的前缀表达式倒置就是最终结果。示例代码如下:

//本程序只能处理有关运算符+、-、*、/的中缀表达式,不能是÷或者×及其他运算
//界限符只能是英文状态的左右括号即'('、')',操作数只能是整数
//本程序不会检查输入的中缀表达式是否正确,因此请您核验好自己的式子是否正确
#include<stdio.h>
#include<string.h> //strlen的头文件,用于判断字符串长度
#include<stdlib.h> //malloc、free的头文件
#define size 50//假定要转换的中缀表达式的字符数在50个以内
typedef struct Linknode{ //定义链栈及结点
    char data; //数据域
    struct Linknode *next; //指针域
}*LiStack;
bool InitStack(LiStack &S){ //链栈的初始化,不带头结点
    S=NULL; //刚开始没有结点
    return true;
}
bool StackEmpty(LiStack S){ //判断栈空
    return S==NULL;
}
bool Push(LiStack &S,char x){ //将元素x入栈
    Linknode *s=(Linknode *)malloc(sizeof(Linknode)); //创建新结点
    if(s==NULL) //内存不足,创建失败
        return false;
    s->data=x;
    s->next=S; //将结点s作为链栈的栈顶结点
    S=s; //栈顶指针S指向结点s
    return true;
}
bool Pop(LiStack &S,char &x){ //栈顶元素出栈,将值赋给x
    if(S==NULL)
        return false; //栈空则返回NULL
    x=S->data;
    Linknode *p=S;
    S=S->next;
    free(p);
    return true;
}
int main(){
    char temp,a[size],b[size]; //静态数组a、b分别存放要转换的中缀表达式和转换后的前缀表达式,字符变量temp存放弹出的栈顶元素
    scanf("%s",&a); //需要您输入中缀表达式
    LiStack S;//初始化一个栈,用于保存括号和暂时还不能确定运算顺序的运算符
    InitStack(S); //初始化链栈
    int i,j,length=strlen(a); //length为输入的中缀表达式的总长度,i、j分别为静态数组a、b的索引下标
    for(i=length-1,j=0;i>=0;i--){ //从右到左扫描中缀表达式的各个字符,直到末尾
        if(a[i]>=48 && a[i]<=57){ //若当前字符是数字,字符0-9的ACSII码范围是[48,57]
            b[j++]=a[i];
            if(a[i-1]=='+'||a[i-1]=='-'||a[i-1]=='*'||a[i-1]=='/') //若上一个字符是运算符,即+、-、*、/,则b加一个空格,以免不同的操作数混在一起
                b[j++]=' ';
        }
        else if(a[i]==')')
            Push(S,a[i]); //若当前字符是右括号则直接入栈
        else if(a[i]=='('){ //若当前字符是左括号
            while(StackEmpty(S)==0){ //栈非空则不断弹出栈内字符并加入前缀表达式
                Pop(S,temp);
                if(temp==')') //直到弹出右括号停止,注意这个)不加入前缀表达式
                    break;
                b[j++]=temp;
                b[j++]=' '; //加一个空格,从而将字符隔开
            }
        }
        else switch(a[i]){ //若当前字符是运算符
            case '*': case '/':{
                while(StackEmpty(S)==0){ //若栈非空,则弹出栈中优先级低于或等于当前运算符的所有运算符,并将这些运算符加入前缀表达式
                    Pop(S,temp);
                    if(temp=='+'||temp=='-'||temp=='/'||temp=='*'){
                        b[j++]=temp;
                        b[j++]=' '; //加一个空格,从而将字符隔开
                    }
                    else if(temp==')'){ //若栈顶元素是右括号或者是优先级高于当前字符的运算符,则将栈顶元素入栈
                        Push(S,temp);
                        break;
                    }
                }
                Push(S,a[i]); //把当前字符入栈
                break;
            }
            case '-': case '+':{
                while(StackEmpty(S)==0){ //若栈非空,则弹出栈中优先级低于或等于当前运算符的所有运算符,并将这些运算符加入前缀表达式
                    Pop(S,temp);
                    if(temp==')'||temp=='*'||temp=='/'){ //若栈顶元素是右括号或者是优先级高于当前字符的运算符,则将栈顶元素入栈
                        Push(S,temp);
                        break;
                    }
                    else if(temp=='+'||temp=='-'){
                        b[j++]=temp;
                        b[j++]=' '; //加一个空格,从而将字符隔开
                    }
                }
                Push(S,a[i]); //把当前字符入栈
                break;
            }
        }
    }
    while(StackEmpty(S)==0){ //栈非空时依次弹出栈顶元素并加入前缀表达式
        Pop(S,temp);
        b[j++]=temp;
        b[j++]=' '; //加一个空格,从而将字符隔开
    }
    printf("结果是:\n");
    for(i=j-1;i>=0;i--) //b中存放字符的索引区间为[0,j-1]
        printf("%c",b[i]); //倒序输出b中的元素
    printf("\n");
    return 0;
}

运行程序后需要输入待转换的中缀表达式,下面是三个输入输出示例:

输入输出
(((15/(7-(1+1)))*3)-(2+(1+1)))- * /15 -7 +1 1 3 +2 +1 1
(2+((3+4)*5))+2 * +3 4 5
(16+(2*(30/4)))+16 *2 /30 4

3.2 前缀表达式求值

3.2.1 手算

前缀表达式求值的手算步骤:从右往左扫描前缀表达式的每一个字符,每遇到一个运算符,就选择运算符右面距离最近的两个操作数执行对应运算,执行运算时注意两个操作数的左右顺序,得到计算结果后去掉刚刚的两个操作数,将新得到的计算结果放在刚刚的这个运算符的位置并代替之,继续从右到左扫描字符直到扫描完全部字符。扫描结束最后得到的就是最终结果。下面是一个示例:

求值前求值后
- * / 15 - 7 + 1 1 3 + 2 + 1 15
+ 2 * + 3 4 537
+ 16 * 2 / 30 431

3.2.2 机算

需要初始化一个栈,用于存放当前暂时还不能确定运算次序的操作数。用栈实现前缀表达式求值的逻辑过程:

① 从右往左扫描前缀表达式的每一个字符,直到扫描完所有字符;

② 若扫描到的字符是操作数则压入栈,并回到①,否则执行③;

③ 若扫描到的字符是运算符,则连续弹出两个栈顶元素(注意:先出栈的是“左操作数”),然后对这两个操作数执行相应运算,然后再将运算结果入栈,回到①。

若被扫描的前缀表达式是合法的,则最后栈中只会留下一个元素,就是最终结果。示例代码如下:

//本程序只能处理有关运算符+、-、*、/的前缀表达式,不能是÷或者×及其他运算
//本程序不会检查输入的前缀表达式是否正确,因此请您核验好自己的式子是否正确
//操作数长度在10位以内且必须是整数
//请将不同的操作数之间、运算符之间、运算符与操作数之间用英文状态的逗号即","隔开
#include<stdio.h>
#include<math.h> //pow的头文件,用于求乘方数
#include<string.h> //strlen的头文件,用于判断字符串长度
#include<stdlib.h> //malloc、free的头文件
#define size 50//假定要求值的前缀表达式的字符数在50个以内
#define num_length 10 //假定前缀表达式中的每一个操作数最长为10位数
typedef struct Linknode{ //定义链栈及结点
    float data; //数据域
    struct Linknode *next; //指针域
}*LiStack;
bool InitStack(LiStack &S){ //链栈的初始化,不带头结点
    S=NULL; //刚开始没有结点
    return true;
}
bool Push(LiStack &S,float x){ //将元素x入栈
    Linknode *s=(Linknode *)malloc(sizeof(Linknode)); //创建新结点
    if(s==NULL) //内存不足,创建失败
        return false;
    s->data=x;
    s->next=S; //将结点s作为链栈的栈顶结点
    S=s; //栈顶指针S指向结点s
    return true;
}
bool Pop(LiStack &S,float &x){ //栈顶元素出栈,将值赋给x
    if(S==NULL)
        return false; //栈空则返回NULL
    x=S->data;
    Linknode *p=S;
    S=S->next;
    free(p);
    return true;
}
int main(){
    char a[size]; //静态数组a存放要处理的前缀表达式
    float right,left,tmp=0; //right,left为弹出栈的元素,依次为右操作数和左操作数
    int b[num_length];//静态数组b存放操作数的各位数字如231的2、3、1
    scanf("%s",&a); //需要您输入前缀表达式
    LiStack S;//初始化一个栈,用于存放当前暂时还不能确定运算次序的操作数
    InitStack(S); //初始化链栈
    int i,j=0,length=strlen(a); //length为输入的前缀表达式的总长度,i、j分别为静态数组a、b的索引下标
    for(i=length-1;i>=0;i--){ //从右到左扫描
        if(a[i]>=48 && a[i]<=57){ //若当前字符是数字,字符0-9的ACSII码范围是[48,57]
            b[j++]=a[i]-48; //字符型的数字减去48就是数字本身,如ASCII码为51的字符为"3",而"3"-48=51-48=3就是数字3本身
            if(a[i-1]==','||a[i-1]=='+'||a[i-1]=='-'||a[i-1]=='*'||a[i-1]=='/'){ //若上一个字符是逗号或者运算符,则代表凑够一个操作数了
                int m=j;
                for(int k=0;k<m;k++) // 将分散的各位数字组成一个操作数
                    tmp+=b[k]*int(pow(10,k));
                Push(S,tmp); //将当前操作数入栈
                tmp=0; //在遇到下一个操作数之前将tmp值归零
                j=0;
            }
        }
        else switch(a[i]){ //若当前字符是运算符
            case '*': case '/': case '-': case '+':{
                Pop(S,left);  //先出栈的是“左操作数”
                Pop(S,right); //后出栈的是“右操作数”
                if(a[i]=='+')
                    Push(S,left+right); //执行运算之后将结果入栈,下面三个也是这样
                else if(a[i]=='-')
                    Push(S,left-right);
                else if(a[i]=='*')
                    Push(S,left*right);
                else if(a[i]=='/')
                    Push(S,left/right);
            }
        }
    }
    Pop(S,tmp); //若被扫描的前缀表达式是合法的,则最后栈中只会留下一个元素,就是最终结果
    printf("结果是:%.2f\n",tmp); //输出结果
    return 0;
}

运行程序后需要输入待求值的前缀表达式,注意本程序只能处理有关运算符+、-、*、/的前缀表达式,不能是÷或者×及其他运算,本程序不会检查输入的前缀表达式是否正确,因此请您核验好自己的式子是否正确,操作数长度在10位以内且必须是整数,请将不同的操作数之间、运算符之间、运算符与操作数之间用英文状态的逗号即","隔开。下面是三个输入输出示例:

输入输出
+,16,*,2,/,30,431.00
+,2,*,+,3,4,537.00
-,*,/,15,-,7,+,1,1,3,+,2,+,1,15.00

3.3 前缀表达式转中缀表达式

3.3.1 手算

前缀表达式转换成中缀表达式的手算步骤:从右往左扫描前缀表达式的每一个字符,每遇到一个运算符,就选择运算符右面距离最近的两个操作数,将【运算符 左操作数 右操作数】变为(左操作数 运算符 右操作数)的形式。注意两个操作数的左右顺序,得到一个式子后去掉刚刚的两个操作数,将得到的式子放在刚刚的这个运算符的位置并代替之,继续从右到左扫描字符直到扫描完全部字符。扫描结束最后得到的式子就是最终结果。下面是一个示例:

转换前的前缀表达式转换后的中缀表达式
- * / 15 - 7 + 1 1 3 + 2 + 1 1(((15/(7-(1+1)))*3)-(2+(1+1)))
+ 16 * 2 / 30 4(16+(2*(30/4)))
+ 2 * + 3 4 5(2+((3+4)*5))

3.3.2 机算

需要初始化一个栈,用于存放当前暂时还不能确定运算次序的操作数。用栈实现前缀表达式转中缀表达式的逻辑过程:

① 从右往左扫描下一个元素,直到处理完所有元素;

② 若扫描到操作数则压入栈,并回到①,否则执行③;

③ 若扫描到运算符,则弹出两个栈顶元素(注意:先出栈的是“左操作数”),构成(左操作数 运算符 右操作数),然后将其压回栈顶,回到①。

若被扫描的前缀表达式是合法的,则最后栈中只会留下一个元素,就是最终结果。示例代码如下:

//本程序只能处理有关运算符+、-、*、/的前缀表达式,不能是÷或者×及其他运算
//本程序不会检查输入的前缀表达式是否正确,因此请您核验好自己的式子是否正确
//操作数长度在10位以内且必须是整数
//请将不同的操作数之间、运算符之间、运算符与操作数之间用英文状态的逗号即","隔开
#include<stdio.h>
#include<string.h> //strlen的头文件,用于判断字符串长度
#include<stdlib.h> //malloc、free的头文件
#define size 50//假定输入的前缀表达式的字符数在50以内
#define num_length 10 //假定每一个操作数最长为10位数
typedef struct Linknode{ //定义链栈及结点
    char data[size]; //数据域
    struct Linknode *next; //指针域
}*LiStack;
bool InitStack(LiStack &S){ //链栈的初始化,不带头结点
    S=NULL; //刚开始没有结点
    return true;
}
bool Push(LiStack &S,char *x){ //将元素x入栈
    Linknode *s=(Linknode *)malloc(sizeof(Linknode)); //创建新结点
    if(s==NULL||strlen(x)>size) //内存不足或者长度超过size,则失败
        return false;
    int i;
    for(i=0;i<strlen(x);i++)
        s->data[i]=x[i];
    s->data[i]='\0';
    s->next=S; //将结点s作为栈顶结点
    S=s; //栈顶指针S指向结点s
    return true;
}
bool Pop(LiStack &S,char *x){ //栈顶元素出栈,将值赋给x
    if(S==NULL)
        return false; //栈空则返回NULL
    Linknode *p=S; //p指向栈顶结点
    int i;
    for(i=0;p->data[i]!='\0';i++)
        x[i]=p->data[i];
    x[i]='\0';
    S=p->next;
    free(p);
    return true;
}
int main(){
    char a[size]; //静态数组a存放要处理的前缀表达式
    char right[size],left[size],tmp[size]; //right,left为弹出栈的元素,依次为右操作数和左操作数;tmp是临时变量,用于存储某些字符串
    char b[num_length];//静态数组b存放操作数的各位数字如231的'2'、'3'、'1'
    scanf("%s",&a); //需要您输入前缀表达式
    LiStack S;//初始化一个栈,用于存放当前暂时还不能确定运算次序的字符串形式的操作数
    InitStack(S); //初始化链栈
    int i,j=0,length=strlen(a); //length为输入的前缀表达式的总长度,i、j分别为静态数组a、b的索引下标
    for(i=length-1;i>=0;i--){ //从右到左扫描
        if(a[i]>=48&&a[i]<=57){ //若当前字符是数字,字符0-9的ACSII码范围是[48,57]
            b[j++]=a[i];
            if(a[i-1]==','||a[i-1]=='+'||a[i-1]=='-'||a[i-1]=='*'||a[i-1]=='/'){ //若下一个字符是逗号或者运算符,则代表凑够一个操作数了
                int m;
                for(m=0;m<j;m++)
                    tmp[m]=b[j-1-m]; //因为是从右到左扫描的,因此需要将数字颠倒一下
                tmp[m]='\0';
                Push(S,tmp); //将当前操作数入栈
                j=0; //将j归零
            }
        }
        else switch(a[i]){
            case '*': case '/': case '-': case '+':{ //若当前字符是运算符
                Pop(S,left);  //先出栈的是“左操作数”
                Pop(S,right); //后出栈的是“右操作数”
                if(strlen(left)+strlen(right)+1+2>size) // 左右操作数长度之和再加上操作符、左右括号的长度过长,容不下
                    return false;
                tmp[0]='('; //先存左括号
                int k;
                for(k=0;k<strlen(left);k++)
                    tmp[k+1]=left[k]; //存左操作数
                tmp[strlen(left)+1]=a[i]; //存运算符
                for(k=0;k<strlen(right);k++)
                    tmp[k+strlen(left)+2]=right[k]; //存右操作数
                tmp[strlen(left)+strlen(right)+2]=')'; //最后存右括号
                tmp[strlen(left)+strlen(right)+3]='\0';
                Push(S,tmp); //将结果入栈
            }
        }
    }
    Pop(S,tmp); //若被扫描的前缀表达式是合法的,则最后栈中只会留下一个元素,就是最终结果
    printf("结果是:%s\n",tmp); //输出结果
    return 0;
}

运行程序后需要输入待转换的前缀表达式,注意本程序只能处理有关运算符+、-、*、/的前缀表达式,不能是÷或者×及其他运算,本程序不会检查输入的前缀表达式是否正确,因此请您核验好自己的式子是否正确,操作数长度在10位以内且必须是整数,请将不同的操作数之间、运算符之间、运算符与操作数之间用英文状态的逗号即","隔开。下面是三个输入输出示例:

输入输出
+,16,*,2,/,30,4(16+(2*(30/4)))
+,2,*,+,3,4,5(2+((3+4)*5))
-,*,/,15,-,7,+,1,1,3,+,2,+,1,1(((15/(7-(1+1)))*3)-(2+(1+1)))

END

实现中缀表达式后缀表达式前缀表达式的图形化界面,需要使用一些第三方库来实现界面设计和用户交互。下面以Qt为例,给出一个简单的实现过程: 1. 创建一个Qt项目并添加一个主窗口,用于显示图形界面。 2. 在主窗口上添加一些控件,如文本框、按钮等,用于输入中缀表达式、显示后缀表达式前缀表达式等。 3. 在按钮的点击事件中编写代码实现中缀表达式后缀表达式前缀表达式的功能。可以使用栈来实现转换过程。 4. 将转换后的后缀表达式前缀表达式显示在相应的文本框中。 下面是一个简单的示例代码,用于实现中缀表达式后缀表达式前缀表达式的图形化界面: ```cpp #include <QMainWindow> #include <QLineEdit> #include <QPushButton> #include <QLabel> #include <QHBoxLayout> #include <QStack> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { // 创建控件 infixEdit = new QLineEdit(this); postfixLabel = new QLabel(this); prefixLabel = new QLabel(this); convertBtn = new QPushButton("转换", this); // 创建布局 QHBoxLayout *layout = new QHBoxLayout(); layout->addWidget(infixEdit); layout->addWidget(convertBtn); layout->addWidget(postfixLabel); layout->addWidget(prefixLabel); // 创建中缀表达式后缀表达式前缀表达式的连接 connect(convertBtn, &QPushButton::clicked, this, &MainWindow::convert); // 设置布局 QWidget *widget = new QWidget(this); widget->setLayout(layout); setCentralWidget(widget); } public slots: void convert() { QString infix = infixEdit->text(); QString postfix = infixToPostfix(infix); QString prefix = infixToPrefix(infix); postfixLabel->setText(postfix); prefixLabel->setText(prefix); } private: QLineEdit *infixEdit; QLabel *postfixLabel; QLabel *prefixLabel; QPushButton *convertBtn; QString infixToPostfix(const QString &infix) { QStack<QChar> stack; QString postfix; for (int i = 0; i < infix.length(); i++) { QChar ch = infix[i]; if (ch.isDigit() || ch.isLetter()) { postfix += ch; } else if (ch == '(') { stack.push(ch); } else if (ch == ')') { while (!stack.isEmpty() && stack.top() != '(') { postfix += stack.pop(); } if (!stack.isEmpty() && stack.top() == '(') { stack.pop(); } } else { while (!stack.isEmpty() && precedence(stack.top()) >= precedence(ch)) { postfix += stack.pop(); } stack.push(ch); } } while (!stack.isEmpty()) { postfix += stack.pop(); } return postfix; } QString infixToPrefix(const QString &infix) { QString reversed = reverse(infix); QString postfix = infixToPostfix(reversed); QString prefix = reverse(postfix); return prefix; } int precedence(QChar op) { if (op == '+' || op == '-') { return 1; } else if (op == '*' || op == '/') { return 2; } return 0; } QString reverse(const QString &str) { QString reversed; for (int i = str.length() - 1; i >= 0; i--) { reversed += str[i]; } return reversed; } }; ``` 在上面的代码中,我们创建了一个MainWindow类,并在其中添加了一个用于输入中缀表达式的文本框、一个用于显示后缀表达式的标签、一个用于显示前缀表达式的标签和一个用于转换的按钮。在按钮的点击事件中,我们调用了infixToPostfix()和infixToPrefix()函数,分别实现中缀表达式后缀表达式前缀表达式的功能。其中,infixToPostfix()函数使用了栈来实现转换过程,infixToPrefix()函数则先将中缀表达式翻转,再调用infixToPostfix()函数,最后再将结果翻转回去得到前缀表达式。 这样,我们就实现了一个简单的中缀表达式后缀表达式前缀表达式的图形化界面。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值