中缀表达式
我们平时使用的表达式是中缀表达式,也就是说运算符都是在中间的,举个例子来说:
(56 - 20)/ (4 + 2)
中缀表达式是人们常用的算术表示方法。而且它是通过括号改变运算时候的优先级的。虽然我们看起来比较直观,但是对于机器来说,这样的表达式是很难运算的。
后缀表达式
所谓后缀表达式,就是将运算符放在操作数的后面,比如1+2*3的后缀表达式为123*+。(这里为了区分操作数,我们用#号表示操作数的结束,也就是1#23#*+)
在后缀表达式中,已经考虑了运算符的优先级(通过顺序已经表现出来了),而且没有括号,只有操作数和运算符。所以对机器来说就容易运算了。
从中缀表达式变成后缀表达式,我们可以使用栈的方式来完成。
算法描述:
将中缀表达式转换为等价的后缀表达式的过程要使用一个栈放“(”,具体可以按照下面的方式进行。
(1)从左到右依次扫描中缀表达式的每一个字符,如果是数字字符和圆点“.”则直接将它们写入后缀表达式中。
(2)如果遇到的是开括号“(”,则将它们压入一个操作符栈(不需要与栈顶操作符相比较),它表明一个新的计算层次的开始,在遇到和它匹配的闭括号“)”时,将栈中的元素弹出来并放入后缀表达式中,直到栈顶元素为“(”时,将栈顶元素“(”弹出(不需要加入后缀表达式),表明这一层括号内的操作处理完毕。
(3)如果遇到的是操作符,则将该操作符和操作符栈顶元素比较:
1、当所遇到的操作符的优先级小于或等于栈顶元素的优先级时,则取 出栈顶元素放入后缀表达式,并弹出该栈顶元素,反复执行直到当前操作符的优先级大于栈顶元素的优先级小于;
2、当所遇到的操作符的优先级大于栈顶元素的优先级的时则将它压入栈中。
可能这样不是很清楚,自己在百度百科里面找到下面的方法描述看起来比较清楚一点:
人工实现转换
这里我给出一个中缀表达式:a+b*c-(d+e)
第一步:按照运算符的优先级对所有的运算单位加括号:式子变成了:((a+(b*c))-(d+e))
第二步:转换前缀与后缀表达式
前缀:把运算符号移动到对应的括号前面
则变成了:-( +(a *(bc)) +(de))
把括号去掉:-+a*bc+de 前缀式子出现
后缀:把运算符号移动到对应的括号后面
则变成了:((a(bc)* )+ (de)+ )-
把括号去掉:abc*+de+- 后缀式子出现
发现没有,前缀式,后缀式是不需要用括号来进行优先级的确定的。如表达式:3+(2-5)*6/3
下面是自己用c语言写的栈的数据结构完成从中缀表达式到后缀表达式的转换以及求值的运算:
#include <stdio.h>
#define MaxSize 100
#define ElemType char
typedef struct
{
ElemType data[MaxSize];
int top;
}SqStack;
void trans(char *exp, char postexp[]);
float compvalue(char *postexp);
int main()
{
char exp[MaxSize];
char postexp[MaxSize], final[MaxSize];
printf("请输入中缀表达式\n");
scanf("%s", exp);
trans(exp, postexp);//exp存放中缀表达式,postexp存放后缀表达式
printf( "后缀表达式的值\n");
printf("%.2f\n", compvalue(postexp));
return 0;
}
void trans(char *exp, char postexp[])
{
SqStack op;
int i = 0, j = 0;
op.top = -1;
while (*exp != '\0')
{
switch (*exp)
{
case '('://左括号进栈
op.top++;
op.data[op.top] = *exp;
exp++;
break;
case ')'://将op栈中“(”以前的运算符依次删除存入数组exp中,然后将“(”删除
while (op.data[op.top] != '(')
{
postexp[i++] = op.data[op.top];
op.top--;
}
op.top--;//"("退栈
exp++;
break;
case '+':
case '-':
while (op.top != -1 && op.data[op.top] != '(')
{//将栈中"("前面的运算符退栈并存放到postexp中
postexp[i++] = op.data[op.top];
op.top--;
}
op.top++;
op.data[op.top] = *exp;
exp++;
break;
case '*':
case '/':
while (op.data[op.top] == '*' || op.data[op.top] == '/')
{//将栈中的“*”或者是“/”运算符依次出栈并存放到postexp中
postexp[i++] = op.data[op.top];
op.top--;
}
op.top++;
op.data[op.top] = *exp;
exp++;
break;
case ' ':break;
default:
while (*exp >= '0' && *exp <= '9')
{
postexp[i++] = *exp;
exp++;
}
postexp[i++] = '#';
}
}
while (op.top != -1)//最后的扫描工作
{
postexp[i++] = op.data[op.top];
op.top--;
}
postexp[i] = '\0';
printf("后缀表达式\n");
for (j = 0; j < i; j++)
printf("%c", postexp[j]);
printf("\n");
}
float compvalue(char *postexp)
{
SqStack st;
float a, b, c, d;
st.top = -1;
while(*postexp != '\0')
{
switch(*postexp)
{
case '+':
a = st.data[st.top];
st.top--;
b = st.data[st.top];
st.top--;
c = a + b;
st.top++;
st.data[st.top] = c;
break;
case '-':
a = st.data[st.top];
st.top--;
b = st.data[st.top];
st.top--;
c = b - a;
st.top++;
st.data[st.top] = c;
break;
case '*':
a = st.data[st.top];
st.top--;
b = st.data[st.top];
st.top--;
c = a * b;
st.top++;
st.data[st.top] = c;
break;
case '/':
a = st.data[st.top];
st.top--;
b = st.data[st.top];
st.top--;
if (a != 0)
{
c = b / a;
st.top++;
st.data[st.top] = c;
}
else
{
printf("\n\t除零错误!\n");
exit(0);
}
break;
default://进行最后的扫尾工作,将数字字符转换成数值存放到d中
d = 0;
while (*postexp >= '0' && *postexp <='9')
{
d = 10 * d + *postexp - '0';
postexp++;
}
st.top++;
st.data[st.top] = d;
break;
}
postexp++;
}
return (st.data[st.top]);
}
还是举个例子来说明上面这段程序:
我输入的中缀表达式是: (56-20)/(4+2)
op栈(临时存放) postexp(后缀表达式) exp 说明
( (56-20)/(4+2) 遇到字符"(",进栈op
( 5 56-20)/(4+2) 遇到数字“5”,进postexp
( 56 6-20)/(4+2) 遇到数字“6”,进postexp
(- 56# -20)/(4+2) 遇到“-”,先在postexp上加入#表示数字结束,然后将“-”压入op栈中
(- 56#20# 20)/(4+2) 同上,连续遇到数字,将“20”写入postexp中,然后加上“#”表示数字结束
56#20# - )/(4+2) 遇到了")",将op栈中“(”以前的字符 这里是“-” 压入postexp中,然后将“(”清除
/ 56#20# - /(4+2) 遇到“/”,放入op栈中,然后马上出栈。以前一直想不明白优先级是怎么做到的,现在明白了。对于无括号表达式,加减符号一直存放在栈中,而对于乘除符号,压入栈后马上就弹了出来。如果一来解决了乘除优先的问题
/( 56#20# - (4+2) 遇到了“(”,将括号压入op栈中
/( 56#20# -4 4+2) 遇到了数字“4”,直接压入postexp中
/(+ 56#20# -4# +2) 遇到了“+”,先在postexp中加入“#”表示数字结束,然后在op栈中压入“+”
/(+ 56#20# -4#2 2) 遇到了数字“2”,直接压入postexp中
/ 56#20# -4#2#+ ) 遇到了“)”,先在postexp中加入“#表示数字结束,再在op栈中压入“(”以前的字符 这里是“+”
56#20# -4#2#+/ 扫描完毕,将op栈中的字符压入postexp中
吼吼~~~,终于完成了,下面我们来看下规律
1.遇到数字,直接压入postexp栈中,知道出现的字符不是数字,则加“#”表示结束
2.遇到"+" "-" "*" "/" 操作符,直接压入op栈中临时存放
对于无括号表达式,加减符号一直存放在栈中,而对于乘除符号,压入栈后马上就弹了出来。如果一来解决了乘除优先的问题
3.遇到“ (”, 直接压入op栈中
4.遇到")",将op栈中“(”以前的所有字符全部压入postexp栈中,然后将op栈中的"("销毁 ( 注意这里的“(”是与“)”对应的前一个"(" )
至于后缀表达式的求值,相信看了中缀表达式怎么转变成后缀表达式了以后理解求值就不是很难了吧。。。
最后还是说一句:自己是新手,有什么错误恳请大家指教。谢谢............