波兰表达式

逆波兰表达式
逆波兰表达式又叫做后缀表达式。在通常的表达式中,运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。波兰逻辑学家 J.Lukasiewicz于1929年提出了另一种表示表达式的方法。按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。

逆波兰表达式,它的语法规定,表达式必须以逆波兰表达式的方式给出。逆波兰表达式又叫做后缀表达式。这个知识点在数据结构和编译原理这两门课程中都有介绍,下面是一些例子:
正常的表达式      逆波兰表达式
 
a+b   --->  a,b,+   
a+(b-c) --->  a,b,c,-,+   
a+(b-c)*d   --->  a,b,c,-,d,*,+   
a+d*(b-c)     --->  a,d,b,c,-,*,+

逆波兰表达式的用途
它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下: 按顺序扫描逆波兰表达式,如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元
素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

中序表达式转换为逆波兰表达式
1、建立运算符栈stackOperator用于运算符的存储,此运算符在栈内遵循越往栈顶优先级越高的原则
2、预处理表达式,正、负号前加0(如果一个加号(减号)出现在最前面左括号后面,则该加号(减号) 为正负号) 。   
3、顺序扫描表达式,如果当前字符是数字(优先级为0的符号),则直接输出该数字;如果当前字符为运算符或括号(优先级不为0的符号),则判断第4点 。   
4、若当前运算符为'(',直接入栈;
若为')',出栈并顺序输出运算符直到遇到第一个'(',遇到的第一个'('出栈但不输出;
若为其它,比较stackOperator栈顶元素与当前元素的优先级:如果栈顶元素是'(',当前元素入栈如果栈顶元素 >= 当前元素,出栈并顺序输出运算符直到 栈顶元素 < 当前元素,然后当前元素入栈;   如果 栈顶元素 < 当前元素,直接入栈。
5、重复第3点直到表达式扫描完毕。  
6、顺序出栈并输出运算符直到栈元素为空。

上面的算法比较抽象,下面来个实际例子

写一个方法,参数传递一个字符串表达式,返回结果为表达式计算结果。
如:传递表达式"5 + ((1 + 2) * 4) − 3"返回计算的结果。
1.将中缀表达式转换为逆波兰表达式
1)建立一个运算符栈stackOperator用来存放运算符;建立一个字符串链表reversePolishExpression用来存放逆波兰表达式
2)顺序扫描
"5 + ((1 + 2) * 4) − 3",根据算法可以得出stackOperatorreversePolishExpression值的变化过程:

扫描     操作   
stackOperator值    reversePolishExpression值           注释

5           输出               空                                 5                           
当前字符是数字直接输出该数字
+           入栈               +                                  5                             栈顶元素为空,不用比较,入栈
(           入栈                (                                   5                             当前运算符为'(',直接入栈
(           入栈                (  (                                5                             当前运算符为'(',直接入栈
1         
输出                (  (                                5 1                          当前字符是数字直接输出该数字
+          入栈                (  ( +                             5  1                         + 优先级< 栈顶元素 ( ,入栈
2         
输出                (  ( +                             5  1  2                     当前字符是数字直接输出该数字
)           出栈                (                                   5   1  2  +                出栈并顺序输出运算符直到遇到第一个'('
*           入栈                (  *                                5   1  2  +                 * 优先级< 栈顶元素 ( ,入栈
4         
输出                (  *                                5   1  2  +  4             当前运算符为'(',直接入栈
)          
输出                                                 5   1  2  +  4  *          出栈并顺序输出运算符直到遇到第一个'('
-            入栈               
-                                   5   1  2  +  4  *          栈顶元素为空,不用比较,入栈
3         
输出                 -                                   5   1  2  +  4  *  3      当前字符是数字直接输出该数字
最后     输出                空                                  5   1  2  +  4  *  3  -   顺序出栈并输出运算符直到栈元素为空

下表给出了该逆波兰表达式从左至右求值的过程,堆栈栏给出了中间值,用于跟踪算法。

输入操作堆栈注释
5 入栈 5
1 入栈 5, 1
2 入栈 5, 1, 2
+ 加法运算 5, 3 (1, 2)出栈;将结果(3)入栈
4 入栈 5, 3, 4
* 乘法运算 5, 12 (3, 4)出栈;将结果(12)入栈
+ 加法运算 17 (5, 12)出栈;将结果 (17)入栈
3 入栈 17, 3
减法运算 14 (17, 3)出栈;将结果(14)入栈

计算完成时,栈内只有一个操作数,这就是表达式的结果:14

实现

#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#define M 40
/*定义堆栈*/
typedef struct{
 double data[M];
 int top;
}Stack;
/*初始化堆栈*/
InitStack(Stack *s)
{
 s->top=0;
}
/*判断栈是否为空*/
int StEmpty(Stack *s)
{
 if(s->top==0)
 {
   return 1;
 }
 else
 {
   return 0;
 }
}
/*入栈操作*/
StPush(Stack *s,double x)
{
  if(s->top==M)
  {
   printf("The stack is overflow!");
  }
  else
  {
   s->top=s->top+1;
   s->data[s->top]=x;
  }
}
/*出栈操作*/
double StPop(Stack *s)
{
  double t;
  if(!StEmpty(s))
  {
   t=s->data[s->top];
   s->top=s->top-1;
  }
   else
  {
   printf("StPop:The stack is empty!");
   t=NULL;
  }
  return t;
}
/*获取栈顶元素*/
double StGetTop(Stack *s)
{
  double t;
  if(!StEmpty(s))
  {
   t=s->data[s->top];
  }
  else
  {
   printf("StGeTop:The stack is empty!");
   t=NULL;
  }
  return t;
}
/*将数字字符转换成整形*/
int ChrTransferint(char c)
{
  int n;
  switch(c)
  {
    case '0': n=0;break;
    case '1': n=1;break;
    case '2': n=2;break;
    case '3': n=3;break;
    case '4': n=4;break;
    case '5': n=5;break;
    case '6': n=6;break;
    case '7': n=7;break;
    case '8': n=8;break;
    case '9': n=9;break;
  }
   return n;
}
/*获取两个操作符之间数字字符的个数,返回的是最后一个数字字符的位置*/
int GetNumsize(char str[],int n1)
{
  int n2=n1;
  while(isdigit(str[n2])||(str[n2])==46)/*isdigit()判断是否数字字符*/
  {
   n2=n2+1;
  }
  return n2;
}
/*判断上个函数中获得的数字字符串中是否包含小数点,并返回它的位置,不包含,返回-1*/
int IsIncludepoint(char str[],int n1,int n2)
{
  int n3=-1;
  int i;
  for(i=n1;i<=n2;i++)
  {
    if(str[i]=='.')
     {
       n3=i;
       break;
     }
  }
  return n3;
}
/*将数字字符转换成数值*/
double Transfer(char str[],int n1,int n2,int n3)
{
 double data=0;
 int i,ct;
 if(n3<0)
 {
   for(i=n2;i>=n1;i--)
    {
      ct=ChrTransferint(str[i]);
      data=data+ct*pow(10,n2-i);/*pow(x,y)计算x的y次方的值*/
    }
 }
 else
 {
    for(i=n3-1;i>=n1;i--)
    {
      ct=ChrTransferint(str[i]);
      data=data+ct*pow(10,n3-1-i);/*pow(x,y)计算x的y次方的值*/
    }
    for(i=n3+1;i<=n2;i++)
    {
      ct=ChrTransferint(str[i]);
      data=data+ct*pow(0.1,i-n3);/*pow(x,y)计算x的y次方的值*/
    }
 }
 return data;
}
/*主程序*/
main()
{
  char str[M],c;
  char a;
  int n,p1,p2,p3;    /*n为字符串长度,p1,p2,p3分别为数字字符起始位置,结束位置,和小数点位置*/
  double data;      /*存放转换后的数值*/
  int i=0;
  Stack *so=(Stack *)malloc(sizeof(Stack));   /*存储操作符 '(':1,'+':2,'-':3, '*':4,'/':5  字符'),='不压栈*/
  Stack *sd=(Stack *)malloc(sizeof(Stack));   /*存储操作数*/
  InitStack(so);
  InitStack(sd);
  printf("Please input formula(format:(1+2)*1.2/4=):\n");
  n=0;
  while((a=getchar())!='\n')
  {
    str[n]=a;
    n++;
  }
  while(i<n)
   {
     char c;
     c=str[i];
     if(c=='(')
      {                                         /*c若是'('直接入栈so,i++*/
        StPush(so,1);
        i++;
      }
     else if(isdigit(c))
      {
        p1=i;                            /*c若是数字字符,一并将后面的连续数字字符转换为数值并压栈到sd,并把i设为后面的*/
 p2=GetNumsize(str,p1);
 p3=IsIncludepoint(str,p1,p2-1);  /*第一个非数字字符的位置*/
 data=Transfer(str,p1,p2-1,p3);
        StPush(sd,data);
        i=p2;
      }
     else if(c=='+')
       {
 StPush(so,2);          /*c若是'+'直接入栈so,i++*/
        i++;
       }
     else if(c=='-')
       {
        StPush(so,3);          /*c若是'-'直接入栈so,i++*/
        i++;
       }
     else if(c=='*')
       {
        if(str[i+1]=='(')      /*c若是‘*’它后面的字符是否为'(',若是直接将'*'压栈so,*/
          {                  
           StPush(so,4);
           i++;
          }
        else
          {
           double t1,t2,t3;       /*若不是,为数字字符,将后面的连续数字字符一并转换成数值t2,sd出栈给t1,将t3=t2*t1压栈到sd*/
           t1=StPop(sd);           /*操作符'*'不压栈so*/
           p1=i+1;
    p2=GetNumsize(str,p1);
    p3=IsIncludepoint(str,p1,p2-1);
    t2=Transfer(str,p1,p2-1,p3);
           t3=t1*t2;
           StPush(sd,t3);
           i=p2;
           }
       }
     else if(c=='/')
       {
        if(str[i+1]=='(')
          {
           StPush(so,5);
           i++;
          }
        else
          {
           double t1,t2,t3;
           t1=StPop(sd);                    /*c是'/'同'*'*/
           p1=i+1;
    p2=GetNumsize(str,p1);
    p3=IsIncludepoint(str,p1,p2-1);
    t2=Transfer(str,p1,p2-1,p3);
           t3=t1/t2;
           StPush(sd,t3);
           i=p2;
          }
       }
     else if(c==')')
       {
        double t1,t2,t3;
        int p;
        while((p=StPop(so))!=1&&!StEmpty(so))  /*c若是')',出栈so,判断是'+'或'-',出栈sd两个操作数,进行加减运算*/
         {                                     /*直到StPop=='('*/
           t1=StPop(sd);
           t2=StPop(sd);
           if(p==2)
           {
             t3=t2+t1;
             StPush(sd,t3);
           }
           else if(p==3)
           {
             t3=t2-t1;
             StPush(sd,t3);
           }
         }
        if(StGetTop(so)==4)                /*然后判断so栈顶是否为'*'或者'/'*/
         {
           StPop(so);
           t1=StPop(sd);                    /*为'*'出栈so,出栈 sd 获得2个操作数,进行乘法操作*/
           t2=StPop(sd);                  
           t3=t2*t1;
           StPush(sd,t3);
         }
        else if(StGetTop(so)==5)
         {
           StPop(so);
           t1=StPop(sd);         /*为'/'出栈so,出栈 sd 获得2个操作数,进行除法操作*/
           t2=StPop(sd);
           t3=t2/t1;
           StPush(sd,t3);
         }
         i++;
       }
     else if(c=='=')
       {
  double t1,t2,t3;            /*c若是'=',这是so内只有加减号,出栈so到p ,sd到t1,t2*/
  int p;
         while(!StEmpty(so))
          {
           t1=StPop(sd);
           t2=StPop(sd);
    p=StPop(so);
           if(p==2)
           {
             t3=t2+t1;             /*p=='+',加法运算,并将结果t3压栈sd*/
             StPush(sd,t3);
           }
           else if(p==3)
           {
             t3=t2-t1;
      StPush(sd,t3);         /*p=='-',减法运算,并将结果t3压栈sd*/
           }
   }
   i++;
       }
   }
if(!StEmpty(so)||StEmpty(sd))
{
  printf("Input error,Back!\n");  /*若so不为空,或者sd为空,且sd中只有一个元素,则输入的式子不对*/
}
else
{
  double end;
  int i;                                  /*否则,sd中的那个数据就是最后计算结果,打印输出*/
  end=StGetTop(sd);
  printf("The value of this formula:\n");
  for(i=0;i<n;i++)
   {
     printf("%c",str[i]);
   }
  printf("%f\n",end);
}

} 



你可能感兴趣的:(波兰表达式)