对于四则运算,我们大家都很熟悉,不过就是“先乘除,后加减”的运算规则而已,如果遇上括号,则就先算括号里面的,仍然遵循“先乘除,后加减”的原则。比如,有个式子6*(5-4)+10/5,即为典型的四则运算,那么此式子我们平时怎么运算呢?当然是先算括号里面的5-4啊,于是此式子就变成了6*1+10/5。然后再根据运算规则加以变换就成了6+2,最后当然直接算了啊,结果为8。看起来很容易计算,但是这只是对于人而言,要是对于计算机呢?它可不知道什么是“先乘除后加减”啊,所以我们必须采取一些其他的方法让计算机也能像人脑一样快速进行四则运算。20世纪50年代,波兰有位逻辑学家灵感突现,想到了一种不需要括号的后缀表达式,我们称它为逆波兰式。对于刚才的式子6*(5-4)+10/5来说,我们称它为中缀表达式,那么它的逆波兰式是6 5 4 - * 10 5 / + ,称其为后缀表达式的原因在于该式中的运算符号都是在要运算数字的后面出现,所以称其为后缀表达式。
一、中缀表达式转后缀表达式
我们已经知道需要把中缀表达式转换为后缀表达式之后计算机才能进行运算,那么如何进行转换呢?我们把平时所用的标准四则运算表达式叫做中缀表达式,即“6*(5-4)+10/5”,但是它要转换成后缀表达式“6 5 4 - * 10 5 / +”就是一件非常难的事情了。其转换过程一般遵循如下规则:从左到右先遍历中缀表达式的每个数字和符号,若是数字就直接输出,即成为后缀表达式的一部分;若是运算符号,则判断其与栈顶符号的优先级,若是右括号或优先级低于栈顶符号的,则栈顶元素依次出栈并输出,并将当前运算符号进栈,一直到最终输出后缀表达式为止。
对于式子6*(5-4)+10/5而言,其转换过程为:首先遍历中缀表达,先将6输出,作为中缀表达式的一部分,然后是运算符“*”,入栈,即此时栈底元素为“*”,然后是“(”,因其是左括号,还未匹配,所以直接入栈,此时栈中元素有“* (”,然后是数字“5”,输出,接着是符号“-”,入栈,然后是数字“4”,直接输出,接着发现是“)”,需要与之前的“(”进行匹配,所以栈顶元素依次出栈输出,直到“(”出栈为止,此时栈中元素只有“*”,后缀表达式为6 5 4 - ,接下来是符号“+”,因为其优先级低于栈中元素“*”,所以“*”出栈,“+”入栈,此时栈中只有“+”元素,后缀表达式变为6 5 4 - * ,然后是数字“10”,直接输出,接着是符号“/”,其优先级比栈中元素高,所以直接入栈,接着是数字“5”,直接输出,此时中缀表达式已经遍历完了,所以直接将栈顶元素依次输出,直至栈空。此时后缀表达式便为最终的结果6 5 4 - * 10 5 / + 。
此上步骤用图可表示为:
1.初始化一个空栈,用来对符号进出栈使用。
2.遍历中缀表达式 ,第一个字符是6,直接输出。后面是符号“*”,进栈。
3.第3个字符是“(”,因为是左括号,还未配对,所以直接进栈。括号后是数字“5”,直接输出。
4.第5个字符是运算符“-”,入栈。之后是数字“4”,直接输出。
5.接下来是符号“)”,需要与之前的“(”配对,所以,栈中元素依次出栈,直到“(”出栈为止。
6.接下来是运算符“+”,因为其优先级低于栈顶元素“*”,所以栈顶元素出栈,“+”进栈。
7.然后是数字“10”,直接输出。后面是运算符“/”,因为优先级比栈顶元素优先级高,直接入栈。
8.之后是数字“5”,直接输出。此时由于中缀表达式已经遍历完毕,所以栈中元素依次输出。
9.至此,中缀表达式转后缀表达式完成。
二、后缀表达式计算结果
规则:从左到右遍历后缀表达式中的每个数字和符号,遇到数字就进栈,遇到符号,就将处于栈顶的两个数字出栈,并进行运算,将运算结果入栈,直到获得最终结果。
1.初始化一个空栈,用于对要运算的数字进出栈使用。
2.遍历后缀表达式,。后缀表达式中前三个都是数字,所以先将3个数字入栈。
3.接下来是符号“-”,所以将栈顶元素4作为减数,将栈中元素5作为被减数出栈,进行运算,并将运算结果5-4=1入栈。
4.接下来是符号“*”,所以将栈中的元素1和6出栈,进行运算,将运算结果6*1=6入栈。
5.接下来是数字10和5,直接入栈。
6.接下来是符号“/”,所以将栈中元素10和5作为运算数字出栈,然后将结果10/5=2入栈。
7.接下来是符号“+”,所以将栈中元素6和2出栈,将结果8入栈。
8.此时,后缀表达式已经遍历完毕,因此将栈中元素出栈,即为表达式最终结果,栈回复到初始空栈的状态。
代码如下:
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#define MAXSIZE 1024
typedef char ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int top; //栈顶指针
}SequenStack;
//初始化顺序栈
SequenStack* Init()
{
SequenStack* S;
S = (SequenStack *)malloc(sizeof(SequenStack));
S->top = -1;
return S;
}
//判断栈是否为空
int SequenStack_Empty(SequenStack *S)
{
if (S->top == -1) //栈空
{
return 1;
}
else
{
return 0;
}
}
//判断是否栈满
int SequenStack_Full(SequenStack *S)
{
if (S->top + 1 == MAXSIZE) //栈满
{
return 1;
}
else
{
return 0;
}
}
//入栈
int Push_SequenStack(SequenStack *S, ElemType x)
{
//如果栈满
if (S->top >= MAXSIZE - 1)
{
printf("OverFlow!\n");
return 0;
}
S->top++;
S->data[S->top] = x;
return 1;
}
//出栈
int Pop_SequenStack(SequenStack *S, ElemType* x)
{
//如果栈空
if (S->top == -1)
{
printf("Error!\n");
return 0;
}
else
{
S->top--;
*x = S->data[S->top + 1];
return 1;
}
}
//获取栈顶元素
int GetTop_SequenStack(SequenStack *S, ElemType* x)
{
//如果栈空
if (S->top == -1)
{
printf("Error!\n");
return 0;
}
else
{
*x = S->data[S->top];
return 1;
}
}
//中缀表达式转后缀表达式
char *TransIntoPostfixExpression(char* express)
{
char operation; //存储运算符
char oprand; //存储操作数或运算符
SequenStack* S;
char* postfixexpression; //后缀表达式指针
int offset = 0;
S = Init();
int len = strlen(express);
postfixexpression = (char *)malloc(len * sizeof(char));
//遍历中缀表达式,如果是数字,就直接存入后缀表达式,否则根据运算符的优先级进行出入栈操作
for (int i = 0; express[i] != '\0'; i++)
{
oprand = express[i];
switch (oprand)
{
//如果是'+'、'-',则将栈中'('前的运算符出栈并存入后缀表达式
case '+':
case '-':
while (!SequenStack_Empty(S))
{
GetTop_SequenStack(S, &operation);
if (operation != '(')
{
Pop_SequenStack(S, &operation);
postfixexpression[offset++] = operation;
}
else
{
break;
}
}
Push_SequenStack(S, oprand);
break;
//如果是'*'、'/',则将栈顶为'*'、'/'的运算符出栈并存入后缀表达式
case '*':
case '/':
while (!SequenStack_Empty(S))
{
GetTop_SequenStack(S, &operation);
if (operation == '*' || operation == '/')
{
Pop_SequenStack(S, &operation);
postfixexpression[offset++] = operation;
}
else
{
break;
}
}
Push_SequenStack(S, oprand);
break;
//如果是‘(’,则将‘(’入栈
case '(':
Push_SequenStack(S, oprand);
break;
//如果是‘)’,则将‘)’前的运算符出栈并存入后缀表达式中
case ')':
while (!SequenStack_Empty(S))
{
GetTop_SequenStack(S, &operation);
if (operation != '(')
{
Pop_SequenStack(S, &operation);
postfixexpression[offset++] = operation;
}
else
{
Pop_SequenStack(S, &operation);
break;
}
}
break;
//如果是数字,则直接存入后缀表达式
default:
postfixexpression[offset++] = oprand;
}
}
//遍历完中缀表达式后,将栈中剩余运算符出栈,并存入后缀表达式中
while (!SequenStack_Empty(S))
{
Pop_SequenStack(S, &operation);
postfixexpression[offset++] = operation;
}
postfixexpression[offset] = '\0';
return postfixexpression;
}
//将字符转换为数字
int ChangeCharToData(char c)
{
return c - '0';
}
//将数字转换为字符
char ChangeDataToChar(int x)
{
return x + 48;
}
//根据操作符和操作数进行四则运算
int Arithmetical(int operand1, int operand2, char operation)
{
int result;
switch (operation)
{
case '+':
result = operand1 + operand2;
break;
case '-':
result = operand1 - operand2;
break;
case '*':
result = operand1 * operand2;
break;
case '/':
result = operand1 / operand2;
break;
}
return result;
}
//根据操作符进行求值运算
void Calculate(SequenStack* S, char operation)
{
char operand1, operand2; //存储操作数1和操作数2
int result;
Pop_SequenStack(S, &operand2);
Pop_SequenStack(S, &operand1);
//根据运算符operation,调用Arithmetical函数进行运算
result = Arithmetical(ChangeCharToData(operand1), ChangeCharToData(operand2), operation);
Push_SequenStack(S, ChangeDataToChar(result));
}
//根据后缀表达式求值
int EvaluatePostfixExpression(char* express)
{
char oprand; //存储后缀表达式中的元素
SequenStack* S;
char result;
S = Init();
for (int i = 0; express[i] != '\0'; i++)
{
oprand = express[i];
if (oprand >= '0' && oprand <= '9')
{
Push_SequenStack(S, oprand);
}
else
{
Calculate(S, oprand);
}
}
Pop_SequenStack(S, &result);
return ChangeCharToData(result);
}
void main()
{
char* infixExpression; //中缀表达式指针
char* postfixExpression; //后缀表达式指针
int result = 0;
infixExpression = (char*)malloc(sizeof(char));
printf("请输入中缀表达式:");
gets(infixExpression);
postfixExpression = TransIntoPostfixExpression(infixExpression);
printf("转换为后缀表达式为:%s\n", postfixExpression);
result = EvaluatePostfixExpression(postfixExpression);
printf("计算结果是:%d\n", result);
}
经大佬提醒,验证之后发现此代码只支持一位数的加减乘除,当时写的时候没有写太多的测试用例,文中写的这个 6*(5-4)+10/5 式子也没有去验证结果。经大佬提醒,才发现代码错误,但是整个处理的过程是没有问题的,后续会抽时间对代码进行优化,或许会出一个 Java 版本的~
多位数的计算请参考:栈的应用--------表达式求值_孔晨瑞的博客-CSDN博客