在介绍前缀表达式和后缀表达式之前我们先说我们最熟悉的中缀表达式:例如,(3+4)*5-6,这就是中缀表达式,是人经常使用的运算表达式,最容易让人理解,但是对于计算机来说就不太友好了,计算机对前缀表达式和后缀表达式(也叫逆波兰式)比较好处理,上述例子中的中缀表达式对应的前缀表达式和后缀表达式分别是:' -*+3456 ' 和 '34+5*6- ' ,那么我们怎样通过中缀表达式转换成前缀或后缀表达式从而写出算法让计算机计算这个表达式呢,为大家介绍一种比较简单的方法,使用二叉树的遍历方法。
将中缀表达式转换为前缀表达式
第一步:以(3+4)*5-6为例,显然我们可以将这个表达式大致看作三个部分组合而成:(3+4)
5 ,6 这三个部分以及其余的运算符组成 ,相当于可以看作是a*b-c。
第二步:先画出优先级最高的运算表达式,在上述例子中优先级最高的是(3+4),因此我们先画这个部分的二叉树。将运算符左边的数字作为左孩子,右边的数字作为右孩子,运算符号为根节点(不要括号)。如下图所示:
第三步:接下来再找剩下的表达式中优先级最高的表达式,由于我们已经 将(3+4)画好了,所以就将它看为一个整体a,剩下的表达式中最高的优先级表达式为a*5,因此我们接下来画出a*5。相同的方法,先将运算符 * 左边的数字a当作左子树,将右边的数字5作为右子树,将运算符*作为根节点,如下图所示:
第四步:再从剩余的表达式中找出最高优先级的表达式,将已经画出来的表达式看作一个整体 b,剩下的表达式就为 ' b-6 ',继续重复将运算符左边的数字作为左孩子,右边的数字作为右孩子,运算符 ' - '作为根节点,画出二叉树,如下图所示:
至此就完成了中缀表达式的二叉树形式,就算有更复杂的表达式也是利用这种方法画出来,我们现在要的是前缀表达式和后缀表达式,前缀表达式的求法:将中缀表达式的二叉树形式进行前序遍历。后缀表达式的求法:将中缀表达式的二叉树形式进行后序遍历。因此利用大家学过的二叉树遍历的知识很快就可以得到前缀表达式为:'-*+3456',后缀表达式为:'34+5*6-'。
以上操作就将我们熟悉的表达式转换成了方便计算机操作的表达式(虽然看起来怪怪的),那么怎样让计算机帮我们算出这个表达式的值呢?那就是利用栈。
用前缀表达式进行计算的思路:
1:我们先将得到的前缀表达式以字符串的形式保存在一个数组中
2:从最后一位开始向前遍历,如果是数字就将该数字进栈
3:如果是运算符就不进栈,而是将栈中的栈顶元素和次栈顶元素出栈,并将 这两个元素和这个运算符进行运算,再将得到的结果进行入栈操作,继续向前 遍历重复第三个步骤,直到字符串遍历完为止。
就像刚刚举的例子:前缀表达式为' -*+3456 ',先将6,5,4,3进栈,当遍历到'+'运算符时,将3,4出栈,且进行 3+4 运算,将结果7进栈 ,继续遍历到 ' * '运算符,将7 和 5 出栈,并进行 7*5 运算,将结果35 进栈 ,继续遍历 ' - '运算符,将35 和 6 出栈并进行 35-6 运算,将结果29进栈,由于已经遍历完前缀表达式,所以最后的结果就是栈顶元素,取出即可。代码如下:
#include <stdio.h>
#define MAXSIZE 10
struct Stack //定义栈的结构体
{
char arr[MAXSIZE];
int top;
}stack;
void Push_stack(char num) //压栈操作即进栈
{
if(stack.top < MAXSIZE)
{
stack.top++;
stack.arr[stack.top] = num;
}
else
printf("stack over flow!\n");
}
void Pop_stack() //出栈操作
{
if(stack.top >= 0)
{
stack.top--;
}
else
printf("stack under flow!\n");
}
int Count(char a[],int len) //计算表达式
{
stack.top = -1; //初始化栈顶指针
int result;
for(int i = len-1; i >= 0; i--) //从末尾开始遍历前缀表达式
{
if(a[i] >= '0' && a[i] <= '9') //如果是数字字符就进栈
Push_stack(a[i]);
else if (a[i] == '+') //如果是+运算符就进行+运算,并将两个运算的数字出栈
{
result = (stack.arr[stack.top--] - '0') + (stack.arr[stack.top] - '0');
Pop_stack();
Push_stack(result + '0');
}
else if(a[i] == '-') //如果是-运算符就进行-运算,并将两个运算的数字出栈
{
result = (stack.arr[stack.top--] - '0') - (stack.arr[stack.top] - '0');
Pop_stack();
Push_stack(result + '0');
}
else if(a[i] == '*') //如果是*运算符就进行*运算,并将两个运算的数字出栈
{
result = (stack.arr[stack.top--] - '0') * (stack.arr[stack.top] - '0');
Pop_stack();
Push_stack(result + '0');
}
else if(a[i] == '/') //如果是/运算符就进行/运算,并将两个运算的数字出栈
{
result = (stack.arr[stack.top--] - '0') / (stack.arr[stack.top] - '0');
Pop_stack();
Push_stack(result + '0');
}
}
return result; //由于我将每次的结果都进行了保存,因此将结果返回即可
}
int main(int argc, char *argv[])
{
char str[] = "-*+3456"; //前缀表达式
int len = sizeof(str)/sizeof(str[0]); //求出前缀表达式字符串长度
printf("result = %d\n",Count(str,len));
return 0;
}