首页 > 其他分享 >中缀/后缀/前缀表达式及相互转换的手算详细步骤及C代码实现

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

时间:2023-02-05 21:01:25浏览次数:47  
标签:字符 操作数 中缀 后缀 运算符 手算 表达式 前缀


文章目录

  • ​​1 三种算术表达式​​
  • ​​2 后缀表达式相关考点​​
  • ​​2.1 中缀表达式转后缀表达式​​
  • ​​2.1.1 手算​​
  • ​​2.1.2 机算​​
  • ​​2.2 后缀表达式求值​​
  • ​​2.2.1 手算​​
  • ​​2.2.2 机算​​
  • ​​2.3 后缀表达式转中缀表达式​​
  • ​​2.3.1 手算​​
  • ​​2.3.2 机算​​
  • ​​3 前缀表达式相关考点​​
  • ​​3.1 中缀表达式转前缀表达式​​
  • ​​3.1.1 手算​​
  • ​​3.1.2 机算​​
  • ​​3.2 前缀表达式求值​​
  • ​​3.2.1 手算​​
  • ​​3.2.2 机算​​
  • ​​3.3 前缀表达式转中缀表达式​​
  • ​​3.3.1 手算​​
  • ​​3.3.2 机算​​

1 三种算术表达式

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

类型

规则

例1

例2

中缀表达式

运算符在两个操作数中间

a+b

a+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)*5

2 3 4 + 5 * +

16+2*30/4

16 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)*5

2 3 4 + 5 * +

16+2*30/4

16 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 1

5

+ 2 * + 3 4 5

37

+ 16 * 2 / 30 4

31

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,4

31.00

+,2,*,+,3,4,5

37.00

-,*,/,15,-,7,+,1,1,3,+,2,+,1,1

5.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


标签:字符,操作数,中缀,后缀,运算符,手算,表达式,前缀
From: https://blog.51cto.com/u_14975310/6038446

相关文章

  • IOS打开对应后缀文件
    IOS打开对应后缀文件通过ShareExtension打开点击文件共享后出现的上方列表,如下图在info.plist中添加Documenttypes<key>CFBundleDocumentTypes</key> <array>......
  • 文本文档后缀
    文本文档怎么显示后缀1.随意点击一个文件夹,点击选中里边的文本文档(没有就新建一个)2.点击上方查看,在文件扩展名上打勾即可显示后缀如下......
  • 栈实现中缀表达式
    栈实现中缀表达式1.创建两个栈,一个用来存储数字一个用来存储操作符号2.判断每一个字符是数字还是操作符3.如果是数字,直接存入数栈中4.如果是符号,看符号栈是否为空,如果为空直......
  • 逆波兰表达式/中缀表达式
    逆波兰表达式/中缀表达式1.先定义一个方法分割字符串每个数据,然后存到集合里面2.然后在新的方法中定义一个栈来存储数据具体实现如下://字符串转换为list集合publicst......
  • coc.nvim 中 coc-tsserver 和 coc-volar 在 vue 项目打开 ts 后缀文件冲突解决方案
    Ifyouareusing"TakeOverMode"forthefirsttimeinyourprojectTobegin,openthe*.vue,*.ts,*.js,*.tsx,*.jsxfile.Thenrun:CocCommandvolar.initi......
  • 前缀后缀01背包(牛客2023多校D清楚姐姐学01背包)
    0x1f题目:https://ac.nowcoder.com/acm/contest/46812/D0x2f题意:定义初始背包的最优解\(V_{max}\)定义n个物品去掉任意一个后,最优解为\(V_{max}'\)每一个物品\(w......
  • 后缀自动机
    后缀自动机的疑难点代码voidsam_extend(charc){intcur=sz++;st[cur].len=st[last].len+1;intp=last;while(p!=-1&&!st[p].next.count(c))......
  • (AtCoder Beginner Contest 287)(D 字符串前后缀合并匹配)(E Trie求最长公共前缀(LCP)
    D-MatchorNot(字符串前后缀合并匹配)题目大意:给定两个字符串S和T,对于每个x=0,1,2...|T|求S的子串(前x个字符和后|T|-x个字符在不改变顺序的情况下组成)是否与T相......
  • upload-labs pass3,phpstudy中修改httpd.conf后无法解析.php3后缀。phpstudy中64与32系
    问题解决参考自:https://www.likecs.com/show-965809.html 注意:VC运行库(V14-x64)版本必须与Apache、PHP版本相同;VC就是MicrosoftVisualC++,可以通过控制面板查看否则......
  • 汇编语言源码文件后缀.S
    汇编语言源码文件后缀名是.s(不区分大小写,一般是根据约定,比如每个公司要求不一样)但一定是s结尾。   来源:B站《韦东山_嵌入式Linux_第一期ARM裸机实战视频教程_......