在C语言中,将中缀表达式转成后缀表达式,并计算它的结果,具体的思路如下:
1、定义两个栈,numStack用于存放运算对象,最后存放运算结果的,fuStack用于存放运算符号的
2、从左到右遍历字符串
3、如果当前的字符是一个数字,那么先不要着急将其转成数字,然后压入到numStack中,因为考虑到多位数字的情况,所以需要从当前下标开始遍历,一直遍历到第一个运算符出现,那么这时候才可以将运算符前面的数字转成字符串。然后调用atoi,将字符串转成一个数字。这时候,将这个数字输出,同时将其压入到numStack中。
4、如果当前的字符是一个字符,那么这时候需要分几种情况进行讨论:
(1)如果栈为空或者当前符号是一个左括号,直接将当前符号压入到fuStack中
(2)否则,如果栈顶符号是一个左括号,那么直接将当前符号压入到fuStack中
(3)否则,如果当前符号是一个右括号,那么需要将从fuStack中跳出字符,并输出这
个字符,然后从numStack中跳出两个数字,进行相应的运算,将运算结果压numStack
中,重复(3)操作,直到fuStack的栈顶符号是一个左括号,那么这时候,将左括号从
fuStack中跳出,注意不需要输出
(4)否则,当前符号是一个普通运算符的时候,那么这时候,需要比较当前符号和栈
顶符号的优先级,如果当前的符号优先级大于栈顶符号的优先级,那么直接将当前符
号压入到fuStack中,否则,当前符号的优先级小于等于栈顶符号的优先级,那么需要将
栈顶符号跳出,并输出,然后从numStack中跳出两个数字,进行相应的运算,然后将
运算结果压入到numStack中,重复这个步骤,直到当前符号的优先级大于栈顶符号的优
先级才可以将当前符号压入到fuStack中
5、遍历完之后,操作还没有结束,还需要判断fuStack是否为空,如果不为空,那么就从fuStack中跳出栈顶元素,并将其输出,同时,将从numStack中跳出两个数字,进行相应的运算,将运算结果压入到numStack中,重复5的操作,直到fuStack为空。
6、这时候,上述操作都完成之后,这时候numStack的栈顶元素就是整个表达式的运算结果,将其输出即可。
题目的链接:
https://pintia.cn/problem-sets/434/problems/5893
对应的代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX_SIZE 21
#define ERROR 0
#define OK 1
typedef struct Num{
int msg[MAX_SIZE];
int top;
}numStack;
void initStack(numStack *num){
num->top = 0;
}
int push(numStack *num,int e){
if(num->top == MAX_SIZE)
return ERROR;
num->msg[num->top++] = e;
return OK;
}
int pop(numStack *num,int *e){
if(num->top == 0)
return ERROR;
*e = num->msg[--num->top];
return OK;
}
int getTop(numStack *num,int *e){
if(num->top == 0)
return ERROR;
*e = num->msg[num->top - 1];
return OK;
}
int isEmpty(numStack *num){
return num->top == 0;
}
typedef struct FU{
char msg[MAX_SIZE];
int top;
}fuStack;
void initFuStack(fuStack *fu){
fu->top = 0;
}
int pushFu(fuStack *fu,char e){
if(fu->top == MAX_SIZE)
return ERROR;
fu->msg[fu->top++] = e;
return OK;
}
int popFu(fuStack *fu,char *e){
if(fu->top == 0)
return ERROR;
*e = fu->msg[--fu->top];
return OK;
}
int getFuTop(fuStack *fu,char *e){
if(fu->top == 0)
return ERROR;
*e = fu->msg[fu->top - 1];
return OK;
}
int isFuEmpty(fuStack *fu){
return fu->top == 0;
}
int level(char ch){
if(ch == '+' || ch == '-')
return 1;
else if(ch == '*' || ch == '/')
return 2;
else
return 0;
}
int isNum(char ch){
if(ch >= '0' && ch <= '9')
return 1;
return 0;
}
int yunsuan(char ch,numStack *num,int *result){
int a,b;
pop(num,&a);
pop(num,&b);//将从栈中跳出两个元素,并将他们的值赋给a、b
/*
值得注意的是,必须是b - a,b / a,因为栈式先进后出的结构
*/
switch(ch){
case '+':
*result = b + a;
break;
case '-':
*result = b - a;
break;
case '*':
*result = b * a;
break;
case '/':
if(a == 0){
return ERROR;//如果不符合运算法则,那么返回ERROR
}
*result = b / a;
break;
}
return OK;
}
void getPreExpression(numStack *num,fuStack *fu,char *msg){
int i,result,j,k = 0,a,b,count = 0;//count表示数字的个数
char ch,tmp,arr[MAX_SIZE];//arr数组用于多位数字的拼接,ch表示当前的字符,tmp表示fuStack的栈顶元素
for(i = 0; msg[i] != '\0'; i++){
ch = msg[i];
if(isNum(ch)){
j = i;
k = 0;//重置k
//拼接数字
while(isNum(msg[j])){
arr[k++] = msg[j];
j++;
}
arr[k] = '\0';//这一步十分重要
/*
这一步将i回到最后一个数字,因为在for循环中还会进行i++的操作,
如果没有这一步,那么就会导致读取到的字符不全
*/
i = j - 1;
a = atoi(arr);//调用atoi方法,从而将这个多位数字的字符串转成整形数字
count++;
if(count == 1)
printf("%d",a);//如果是第一个数字,那么前面不需要输出一个空格,否则要在前面输出空格
else
printf(" %d",a);
push(num,a);//将数字压入栈中
}else{
//如果是运算符,那么需要分情况讨论
if(isFuEmpty(fu) || ch == '('){
//如果栈为空或者当前字符为左括号,直接将当前符号压入栈中
pushFu(fu,ch);
}else{
/*
如果栈不为空,那么需要讨论几种情况:
①栈顶是左括号,直接将当前字符压入栈中
②否则,如果当前符号是一个右括号,那么将从栈中跳出栈顶元
素,并将其输出,同时将从numStack栈中跳出两个数字,进行相应
的运算,之后将运算结果压入到numStack中,重复③的操作,直到
栈顶符号式左括号,之后将左括号从fuStack中跳出
③否则,当前字符是普通字符,那么需要判断优先级。如果当前符
号的优先级大于栈顶元素的优先级,那么直接入fu栈,否则从符栈
中不断跳出栈顶元素,并将其输出,同时将从numStack中跳出两
个数字,进行相应的运算,然后将运算结果压入到numStack中,
重复此操作,直到当前符号的优先级大于栈顶符号的优先级,才将
当前符号压入到fuStack中
*/
getFuTop(fu,&tmp);
if(tmp == '('){
//如果栈顶元素是一个左括号,那么直接入栈
pushFu(fu,ch);
}else{
//当前的字符不是一个左括号,那么需要判断当前的符号是否为一个右括号
if(ch == ')'){
getFuTop(fu,&tmp);
while(tmp != '('){
popFu(fu,&tmp);
printf(" %c",tmp);
if(!yunsuan(tmp,num,&result)){
printf("被除数不可以为0,运算错误!!!\n");
return;
}
push(num,result);//如果运算符合运算法则,那么将运算结果压入到栈中
getFuTop(fu,&tmp);
}
popFu(fu,&tmp);
}else{
//当前的符号是一个普通符号,那么需要比较优先级
label:
if(isFuEmpty(fu) || level(ch) > level(tmp)){
//当前符号的优先级大于栈顶符号的优先级,那么直接入栈
pushFu(fu,ch);
}else{
//当前的符号的优先级小于等于栈顶符号的优先级,那么将栈顶元素从fu栈中跳出并输出
popFu(fu,&tmp);
printf(" %c",tmp);
if(!yunsuan(tmp,num,&result)){
printf("被除数不可以为0,运算错误!!!\n");
return;
}
push(num,result);//如果运算符合运算法则,那么将运算结果压入到栈中
getFuTop(fu,&tmp);
goto label;//利用goto语句,从而实现当前的符号的优先级小于栈顶符号的优先级,然后将其压入
}
}
}
}
}
}
//将num压入到fu栈中
while(!isFuEmpty(fu)){
popFu(fu,&tmp);
printf(" %c",tmp);
if(!yunsuan(tmp,num,&result)){
//将进行相应的运算,然后将对应的运算结果赋值给result
printf("被除数不可以为0,运算错误!!!\n");
return;
}
push(num,result);//如果运算符合运算法则,那么将运算结果压入到栈中
}
printf("\n");
pop(num,&a);//将num中的栈顶元素的值赋给a,并将其删除
printf("%s的运算结果为%d\n",msg,a);
}
int main(){
numStack *num = (numStack *)malloc(sizeof(numStack));
fuStack *fu= (fuStack *)malloc(sizeof(fuStack));
char msg[MAX_SIZE];
initStack(num);//将栈进行初始化
initFuStack(fu);
scanf("%s",msg);//输入字符串
getPreExpression(num,fu,msg);//将后缀表达式输出,同时将它的运算结果输出
return 0;
}
对应的结果:
不足之处:
1、如果数字是一个小数的情况,则时候就会明显发生错误。
如下图,输入这样的例子,就直接程序结束了.
2、如果一开始输入的就是负数之类的数的话,那么这时候也是不可以进行运算的。比如-5+6等诸如这样的例子,不可以进行运算。
如果有什么好的建议,请大家指正哈!