逆波兰表达式又叫做后缀表达式。在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示表达式的方法。按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。 —–取自百度百科
因为计算机无法像人类一样智能的计算算数表达式,并且即使计算机可以像人类一样计算算式,其耗费的资源与时间是相当大的,所以产生了逆波兰算法。
例如:
中缀表达式 后缀表达式
1+2 1 2+
1+2*3 1 2 3*+
(1+2)*3 1 2 +3*
这就是其运算的原理,将中缀表达式变为后缀表达式,然后再运算,直接从左到右计算,没有中缀表达式那么复杂,为计算机实现复杂的运算提供了可行的方法,下来话不多说,直接上代码:
/*实现逆波兰算法*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int process(char *string);
int calSum(char op,int equation1,int equation2);
int level(char op);
int getEquation(int *stackOfoperand,char *stackOfOperator,int *topOfOperatend,int *topOfOperator);
int main()
{
char equation[50],*str=equation;
int result;
printf("Please enter an equation:\n");
gets(equation);
result=process(equation);
printf("the result is %d\n",result);
return 0;
}
int process(char *string)
{
char stackOfOperator[10],op,*p=string;
int stackOfOperatand[10],topOfOperator=0,topOfOperatand=-1,sum;
stackOfOperator[topOfOperator]='@';
while(*p!='\0'){ //遍历算式
if(*p>='0'&&*p<='9'){ //遇到字符数先转换为整型数再存入操作数的栈中
sum=0;
while(*p>='0'&&*p<='9'){
sum=sum*10+(*p-'0');
p++;
}
stackOfOperatand[++topOfOperatand]=sum; //操作数入栈
}
if(*p=='+'||*p=='-'||*p=='*'||*p=='/') //当遇到操作符时,将算式中的操作符与操作符栈中的操作符相比再
{
while(level(*p)<=level(stackOfOperator[topOfOperator]))
{ //当算式中的操作符的优先级比操作符栈中的低时,栈中的操作符出栈并计算
stackOfOperatand[topOfOperatand]=getEquation(stackOfOperatand,stackOfOperator,&topOfOperatand,&topOfOperator);
topOfOperator--;
}
stackOfOperator[++topOfOperator]=*p++;//当算式中的操作符比操作符栈中的高时直接入栈
}
else if(*p=='(') stackOfOperator[++topOfOperator]=*p++; //当是左括号时直接入栈
else{
if(*p==')'){ //当遇到右括号时前面必有左括号,计算到左括号
while(stackOfOperator[topOfOperator]!='('){
stackOfOperatand[topOfOperatand]=getEquation(stackOfOperatand,stackOfOperator,&topOfOperatand,&topOfOperator);
topOfOperator--;
}
topOfOperator--;//左括号出栈
p++;//指针指向下一个算式中的字符
}
}
}
while(stackOfOperator[topOfOperator]!='@'){ //当跑完字符算式时,将操作符栈跑完的时候操作数栈也跑完了
stackOfOperatand[topOfOperatand]=getEquation(stackOfOperatand,stackOfOperator,&topOfOperatand,&topOfOperator);
topOfOperator--;
}
return stackOfOperatand[topOfOperatand];
}
int calSum(char op,int equation1,int equation2)
{
switch(op){ //遇到操作数计算之后直接返回,不用break
case '+':return equation2+equation1; //算式二是左操作数,算式一是右操作数
case '-': return equation2-equation1;
case '*':return equation2*equation1;
case '/':return equation2/equation1;
}
}
int level(char op) //比较运算符的优先级
{
switch(op){
case '*':
case '/': return 3;
case '+':
case '-': return 2;
case '(': return 1;
case '@': return 0;
}
}
int getEquation(int *stackOfOperand,char *stackOfOperator,int *topOfOperand,int *topOfOperator)
{
char op;
int operand1,operand2;
op=stackOfOperator[*topOfOperator];
operand1=stackOfOperand[*topOfOperand];
*topOfOperand=*topOfOperand-1;
operand2=stackOfOperand[*topOfOperand];
return calSum(op,operand1,operand2);
}
我将操作符和操作数放在了两个栈中,其中在操作符栈进入之前放了一个字符’@’并且通过level函数返回0,因为左括号直接入栈的情况比较特殊,如果第一个是左括号的话,为了避免再讨论当topOfOpertor等于-1和不为-1的情况我在栈顶上先预先放好一个字符’@’,另外该代码是适合输入正确的四则运算的算式,对于错误的算式其结果无法预知。
另外由于在写的几个函数中需要改变主调中的值,我传的都是该值的地址,便于直接修改需要改变的值,也是由于被调需要返回多个值,所以我认为传地址更恰当。