以前写过计算一个字符串的表达式,当时都不知道有逆波兰这东西,- -!真是孤陋寡闻了,不过勉强用其他方法写了出来,用到了递归,不过整体来说很罗索!
逆波兰对这种字符串的表达式计算很容易,只要把一个普通的表达式转换成逆波兰后计算就轻松很多!
首先看一下普通表达式:(1+2)*(2-1) 转成逆波兰后变成 1 2 + 2 1 - *
下面说说怎么转成逆波兰,然后再说计算逆波兰!
假设要转的表达式是一条合法的表达式(这个自己事先写个代码判断表达式的合法性),那么遵照下面的规则就可以转成逆波兰了:
1.首先把普通的表达式按照运算符分离出来放在一个集合E中,比如1+2*3 分离后集合里的元素就是 1 + 2 * 3 五个元素
2.再定义一个集合R(最好是字符串类型的集合,免得后面要转类型),主要是用来存放逆波兰表达式的,还有定义一个堆栈(存储运算符用),最后从左到右遍历集合E
3.遍历E的规则如下:
3.1如果该元素是数字,直接把它添加到集合R中
3.2否则它肯定是运算符,那么再进行判断
3.2.1如果该元素是左括号,或者当时栈为空,那么直接入栈
3.2.2如果该元素是右括号,则把栈内的运算符出栈并添加到集合R中,直到遇到第一个左括号结束(左括号也出栈但不添加到R)
3.2.3否则该元素是普通的运算符(也就是+-*/之类的),那么用该运算符和栈内的运算符号比较优先级(至于怎么比较它们的优
先级,你可以定义一个函数,传一个运算符过去,返回一个int,int值越大优先级越高),如果该运算符的优先级比栈内的运
算符优先级高 或者 栈为空,则直接入栈,否则把栈内的运算符出栈并添加到R中,再判断下个栈内的运算符优先级,直到遇
栈内的运算符优先级<=该运算符或者栈为空时再把该运算符入栈
3.3整个过程完成后,再把栈内的所有运算符出栈并添加到R中
下面看着表达式来理解上面的步骤
运算符优先级:
( ) 1
+ - 2
* / % 3
值越大优先级越高,注意括号优先级是最低的
表达式:2*(1+2/2)
定义集合E储存分离的表达式,E{ 2, *, (, 1, +, 2, /, 2, ) }
定义集合R储存逆波兰, R{...}
定义堆栈S储存运算符, S{...}
从左到右遍历E,开始判断
E[0]是数字2,直接添加到R,R{ 2 }
E[1]是乘号,和栈内运算符比较优先级,这时栈为空,所有直接入栈,S{*}
E[2]是左括号,直接入栈,S{ *, ( }
E[3]是数字1,直接添加到R,R{ 2, 1 }
E[4]是加号,比较栈顶运算符,栈顶为"(",加号比它优先级高,所有直接入栈, S{ *, (, + }
E[5]是数字2,直接添加到R, R{ 2, 1 , 2 }
E[6]是除号,比较栈顶运算符,栈顶为"+",除号>加号,直接入栈, S{ *, (, +, / }
E[7]是数字2,直接添加到R,R{2, 1, 2, 2}
E[8]是右括号,把栈内运算符出栈并添加到R直到遇到第一个左括号,R{ 2, 1, 2, 2, /, + } S{ * },注意左括号出栈但不加到R
那么整个过程就完毕,最后一步把栈内的所有元素添加到R
R{ 2, 1, 2, 2, /, +, * } S{...}
那么表达式2*(1+2/2) 转逆波兰变成 2 1 2 2 / + *
计算逆波兰非常简单,也需要一个堆栈,规则是:
1.从左到右遍历R
2.如果该元素是数字,直接入栈
3.如果该元素是运算符,出栈两个数,计算结果再入栈,逆波兰遍历完后栈内的元素就是表达式的值了
如:R{ 2, 1, 2, 2, /, +, * }
1.数字就入栈,那么S{ 2, 1, 2, 2 }
2.除号,出栈两个2 / 2 = 1,再入栈 S{ 2, 1, 1 }
3.加号,出栈两个1 + 1 = 2,再入栈S{ 2, 2 }
4.乘号,出栈两个2 * 2 = 4,再入栈S{ 4 }
5.逆波兰已遍历完,栈内元素是4,对照下普通表达式的运算结果是否相同?
关于预处理问题
转换成逆波兰前必须先要处理一下
A.比如你要分离普通表达式的时候,负号和减号是相同的,得把减号用其他符号代替,怎么判断是减号还是负号?很简单,从右往左遍历,遇到"-"时再判断它的前一个符,如果是数字或者右括号,那么它就是减号!
B.如果表达式的第一个符是负号,也就是形如-(1+2),那么根据上面第一条的判断它为负号而不是减号,分离这个表达式时就会变成 - ( 1 + 2 ),负号就会被独立分离出来,这样转逆波兰是没有问题,但是计算逆波兰就有问题,它会被转成逆波兰为:- 1 2 +的样子,因为负号会被认为是运算符,所以把栈内元素出栈计算,这时栈根本就是为空的,就会出异常!解决办法是如果表达式的第一个符号为负号,在整个表达式前面加一个0,就会变成
0-(1+2),这样根据A判断"-"就会被当成是减号而不是负号,0减去后面的数不就是负数了嘛!
C.类似第2条,看下形如1*-(-(-(1+2)))这样的表达式,根据A的判断"-"会被当成负号,显然,和B一样,也会出现一样的错误,解决办法还是加0,首先判断如果它是负号,再判断它后面是数字还是运算符,如果是运算符就在负号前面加个0,那么整个下来表达式就会变成1*0-(0-(0-(1+2)))
以上,如发现有错误请指出,有更好的处理办法请提出,谢谢!
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<stack>
#include<math.h>
#include <string.h>
#define max 100
using namespace std;
char ex[max]; /*存储后缀表达式*/
void trans()
{ /*将算术表达式转化为后缀表达式*/
char str[max]; /*存储原算术表达式*/
char stack[max]; /*作为栈使用*/
char ch;
int sum,i,j,t,top=0;
printf("*****************************************\n");
printf("*输入一个求值的表达式,以#结束。*\n");
printf("******************************************\n");
printf("算数表达式:");
i=0; /*获取用户输入的表达式*/
do{
i++;
//cin>>str[i];/*此步我用的是C++ C语言的话在后面 之所以用这个有一点区别 都*/
scanf("%c",&str[i]);
}while(str[i]!='#' && i!=max);
sum=i;
t=1;i=1;
ch=str[i];i++;
//
while(ch!='#')
{
switch(ch)
{
case '(': /*判定为左括号*/
top++;stack[top]=ch;
break;
case ')': /*判定为右括号*/
while(stack[top]!='(')
{
ex[t]=stack[top];top--;t++;
}
top--;
break;
case '+': /*判定为加减号*/
case '-':
while(top!=0&&stack[top]!='(')
{
ex[t]=stack[top];
top--;
t++;
}
top++;
stack[top]=ch;
break;
case '*': /*判定为乘除号*/
case '/':
while(stack[top]=='*'||stack[top]=='/')
{
ex[t]=stack[top];
top--;
t++;
}
top++;
stack[top]=ch;
break;
case ' ':break;
default:
while(ch>='0'&&ch<='9')
{ /*判定为数字*/
ex[t]=ch;t++;
ch=str[i];i++;
}
i--;
ex[t]=' ';t++;
}
ch=str[i];i++;
}
while(top!=0)
{
ex[t]=stack[top];
t++;top--;
}
ex[t]=' ';
printf("\n\t原来表达式:");
for(j=1;j<sum;j++)
printf("%c",str[j]);
printf("\n\t逆波兰式:",ex);
for(j=1;j<t;j++)
printf("%c",ex[j]);
}
void compvalue()
{ /*计算后缀表达式的值*/
float stack[max],d; /*作为栈使用*/
char ch;
int t=1,top=0; /*t为ex下标,top为stack下标*/
ch=ex[t];t++;
while(ch!=' ')
{
switch(ch)
{
case '+':
stack[top-1]=stack[top-1]+stack[top];
top--;
break;
case '-':
stack[top-1]=stack[top-1]-stack[top];
top--;
break;
case '*':
stack[top-1]=stack[top-1]*stack[top];
top--;
break;
case '/':
if(stack[top]!=0) stack[top-1]=stack[top-1]/stack[top];
else
{
printf("\n\t除零错误!\n");
exit(0); /*异常退出*/
}
top--;
break;
default:
d=0;
while(ch>='0'&&ch<='9')
{
d=10*d+ch-'0'; /*将数字字符转化为对应的数值*/
ch=ex[t];t++;
}
top++;
stack[top]=d;
}
ch=ex[t];
t++;
}
printf("\n\t计算结果:%g\n",stack[top]);
}
int main()
{
trans();
compvalue();
system("pause");
return 0;
}