返回首页 一起talk C栗子吧

一起talk C栗子吧(第二十一回:C语言实例--表达式求值)

各位看官们,大家好,前几回中咱们说了堆栈的原理,并且举了实际的例子进行解说,这一回咱们说的例 子是:表达式求值。表达式求值和上一回中说的括号匹配一样,都使用了堆栈的原理,大家可以从例子中 看出来,所以我们把它们放在一起。闲话休提,言归正转。让我们一起talk C栗子吧!

看官们,我们在这里说的表达式为包含加,减,乘除的四则运算表达式。例如:1+23-4/5就是一个四则运 算表达式。这个表达式中,运算符在数字中间,所以我们叫它中缀表达式,这也是符合我们思维的一种表 现形式,不过,计算机就不理解中缀表达式了,因为它没有办法像我们一样先进行乘除运算,然后再进行 加减运算,所以我们需要把中缀表达式转换成计算机能理解的后缀表达式。“什么是后缀表达式”,这时候 台下有看官在提问了,看官莫急,所谓的后缀表达式就是运算符在数字后面。例如:" 1+23-6/3 "这个中缀 表达式可以为" 123*+63/- "这种后缀表达式.

表达式求值有两个大的步骤:

  • 1.中缀表达式转换为后缀表达式。
  • 2.对后缀表达式进行求值。

这两个大的步骤中还有一些小的步骤,接下来我们详细说说这些小步骤。在说之前,我首先说明一个概念: 优先级。优先级代表着先后顺序,举一个日常为生活中的例子:排队买东西的时候,排在队列前面的人, 比排在队列后面人具有优先买东西的权利,我们就可以说:排在队列前面的人买东西的优先级高。优先级 在表达式运算过程中体现为:乘法和除法的优先级比加法和减法的优先级高。也就是我们通常说的先乘除 后加减,这个内容我就不多说了,大家在小学数学中都学过。我们在表达式求值过程中把中缀表达式转换 为后缀表达式也与优先级有关,因为后缀表达式可以去掉运算符的优先级。没有优先级了,计算机就能理 解后缀表达式并对其进行相关的运算。

中缀表达式转换为后缀表达式的步骤如下:

  • 1.从头到尾依次遍历中缀表达式,每次从表达式中读取一个字符;
  • 2.判断步骤1中读取的字符,如果是数字则保存到数组a中,如果是+*等运算符,请看下一个步骤;
  • 3.对存放运算符的栈进行出栈操作,把步骤的2中的运算符和刚才出栈的运算符进行优先级比较;
  • 4.如果步骤2中的运算符优先级高,那么把参与比较的这两个运算符都入栈。否则看下一步;
  • 5.如果步骤2中的运算符优先级低,那么让栈中的运算符继续出栈,并且把出栈的运算符存放数组a中;
  • 6.重复步骤4和步骤5,直到出栈运算符的优先级比步骤2中运算符的优先级高为止;
  • 7.重复步骤1到步骤6.直到遍历完中缀表达式为止;
  • 8.判断栈中是否还有运算符,如果有的话,就把所有运算符出栈,并且把出栈的运算符存放数组a中。

对后缀表达式求值的步骤如下:

  • 1.从头到尾依次遍历后缀表达式,每次从表达式中读取一个字符;
  • 2.判断步骤1中读取的字符,如果是数字则入栈,如果是+*等运算符,请看下一个步骤;
  • 3.对存放数字的栈进行两次出栈操作,依据步骤2中运算符的类型,对出栈的两个数字进行运算;
  • 4.对步骤3中的运算结果进行入栈操作,这样可以把运算结果保存到存放数字的栈中;
  • 5.重复步骤1到步骤4.直到遍历完后缀表达式为止;
  • 6.栈中最后一个元素就是该表达式的运算结果。

看官们,详细的代码如下,请大家参考:

     1  /* **************************
     2   * Head file of Expression Evaluation
     3   * *************************/
     4  #ifndef EXPRESSION_EVALUATION_H
     5  #define EXPRESSION_EVALUATION_H
     6  
     7  #include<stdio.h>
     8  #include<stdlib.h>
     9  
    10  #define SUCCESS 0
    11  #define FAILE 1
    12  
    13  #define LEN 20 //栈的长度先定义为5,需要时可自行修改
    14  
    15  typedef char StackElmt; //把char当作栈中元素的类型,实际中可以使用其它的类型或者自己定义一个元素类型
    16  typedef struct _Stack{
    17      StackElmt *base;
    18      StackElmt *top;
    19      int size;
    20  }Stack;
    21  
    22  StackElmt STACK[LEN+1] = {0}; //顺序存储方式的栈,防止数组越界,最后一个位置不放元素
    23  
    24  //声明函数原型,这里有入栈和出栈操的函数
    25  int StackInit(Stack *s);
    26  int StackDestroy(Stack *s);
    27  int StackPrint(Stack *s);
    28  int StackPush(Stack *s, StackElmt e);
    29  int StackPop(Stack *s, StackElmt *e);
    30  
    31  #endif /* EXPRESSION_EVALUATION_H */
     1  /* **************************
     2   * Soure file of Expression Evaluation
     3   * *************************/
     4  
     5  #include"ExpressionEvaluation.h"
     6  
     7  //实现栈相关的函数,为了方便,放到了主函数所在的文件中,最好是单独建立实现函数的源文件
     8  //初始化中栈
     9  int StackInit(Stack *s)
    10  {
    11      if(NULL == s)
    12          return FAILE;
    13  
    14      s->top = s->base = &STACK[0];
    15      s->size = 0;
    16  
    17  }
    18  int StackDestroy(Stack *s)
    19  {
    20      int i =0;
    21  
    22      if(NULL == s)
    23          return FAILE;
    24  
    25      while(i<LEN)
    26      {
    27          STACK[i] = 0;
    28          i++;
    29      }
    30      s->top = s->base = NULL;
    31      s->size = 0;
    32  }
    33  //输出栈中存放的元素
    34  int StackPrint(Stack *s)
    35  {
    36      int index = 0;
    37  
    38      if(NULL == s)
    39          return FAILE;
    40  
    41      if(s->size == 0)
    42      {
    43          printf("the Stack is empty,there is not any element \n");
    44          return FAILE;
    45      }
    46  
    47      while(index < (s->size))
    48      {
    49          printf("%d  ",*((s->base)+index) );
    50          index++;
    51      }
    52  
    53      printf("\n ");
    54  
    55      return SUCCESS;
    56  }
    57  
    58  //入栈函数,top指向栈顶,先把元素入栈,然后向栈顶移动一位
    59  int StackPush(Stack *s, StackElmt e)
    60  {
    61      if(NULL == s)
    62          return FAILE;
    63  
    64      if(s->size >= LEN)
    65      {
    66          printf("the Stack is full \n");
    67          return FAILE;
    68      }
    69  
    70      *(s->top) = e;
    71      (s->top)++;
    72      (s->size)++;
    73  
    74      return SUCCESS;
    75  }
    76  
    77  //出栈函数,top先向栈底移到一位,然后移出当前它所指向的元素
    78  int StackPop(Stack *s, StackElmt *e)
    79  {
    80      if(NULL == s)
    81          return FAILE;
    82  
    83      if(s->size == 0)
    84      {
    85          //printf("the Stack is empty \n");
    86          return FAILE;
    87      }
    88  
    89      (s->top)--;
    90      *e = *(s->top);
    91      (s->size)--;
    92  
    93      return SUCCESS;
    94  }
    95  
    96  int main()
    97  {
    98      char ch = '\0';
    99      char str[2];
   100      char *a1= "1+2*3-6/3"; // 存放中缀表达式
   101      //char *a1="1+2+3*4";
   102      char a2[LEN]={'\0'}; // 存放后缀表达式
   103      Stack s;
   104      int i,j;
   105      int a,b;
   106      int res = 0;
   107      int stack[LEN] ={0};
   108  
   109      i = j = a = b = 0;
   110      StackInit(&s);
   111  
   112      while(*(a1+i) != '\0') //遍历中缀表达式
   113      {
   114          printf("%c",*(a1+i));
   115  
   116          if(*(a1+i) == '+' || *(a1+i) == '-')
   117          {
   118              if(s.size != 0)
   119              {
   120                  while( s.size >= 0 && !StackPop(&s,&ch))
   121                  {
   122                      *(a2+j) = ch;
   123                      ch = '\0';
   124                      j++;
   125                  }
   126                  StackPush( &s,*(a1+i) );
   127              }
   128              else
   129                  StackPush(&s,*(a1+i));
   130          }
   131          else if( *(a1+i) == '*' || *(a1+i) == '/')
   132          {
   133              if(s.size != 0)
   134              {
   135                  if(!StackPop(&s,&ch))
   136                  {
   137                      if(ch == '+' || ch == '-')
   138                      {
   139                          StackPush(&s,ch);
   140                          StackPush( &s,*(a1+i) );
   141                      }
   142                      else
   143                      {
   144                          StackPush(&s,ch);
   145                          while( !StackPop(&s,&ch) && (ch == '*' || ch == '/') )
   146                          {
   147                              *(a2+j) = ch;
   148                              ch = '\0';
   149                              j++;
   150                          }
   151                          StackPush( &s,*(a1+i) );
   152                      }
   153                  }
   154              }
   155              else
   156                  StackPush(&s,*(a1+i));
   157          }
   158          else //从中缀表达式中读取的字符是数字,保存到数组中
   159          {
   160              *(a2+j) = *(a1+i);
   161              j++;
   162          }
   163  
   164          i++;
   165      }
   166  
   167      while( s.size >= 0 && !StackPop(&s,&ch)) //栈中还有运算符的话,需要全部取出,放到后缀表达式中
   168      {
   169          *(a2+j) = ch;
   170          ch = '\0';
   171          j++;
   172      }
   173      *(a2+j) = '\0';
   174  
   175      i=j=0;
   176      StackDestroy(&s);
   177      StackInit(&s);
   178  
   179      while(*(a2+i) != '\0') //遍历后缀表达式
   180      {
   181          if(*(a2+i) == '+')
   182          {
   183              j--;
   184              a = stack[j];
   185              j--;
   186              b = stack[j];
   187  
   188              stack[j]= a+b;
   189              j++;
   190          }
   191          else if(*(a2+i) == '-')
   192          {
   193              j--;
   194              a = stack[j];
   195              j--;
   196              b = stack[j];
   197  
   198              stack[j]= b-a;
   199              j++;
   200          }
   201          else if(*(a2+i) == '*')
   202          {
   203              j--;
   204              a = stack[j];
   205              j--;
   206              b = stack[j];
   207  
   208              stack[j]= a*b;
   209              j++;
   210          }
   211          else if(*(a2+i) == '/')
   212          {
   213              j--;
   214              a = stack[j];
   215              j--;
   216              b = stack[j];
   217  
   218              stack[j]= b/a;
   219              j++;
   220          }
   221          else
   222          {
   223              str[0] =*(a2+i);
   224              str[1] ='\0';
   225              stack[j]= atoi(str);
   226              j++;
   227          }
   228  
   229          i++;
   230      }
   231      res =stack[0];
   232  
   233      printf(" = %d ",res);
   234      printf("\n");
   235      return SUCCESS;
   236  }

从代码中可以看到,我们用了两次栈,一次是在中缀表达式转换成后缀表达式的过程中,栈用来存放运算 符。另外一次是在后缀表达式求值的过程中,栈用来存放参与运算的数字。

各位看官,关于表达式求值的例子咱们就说到这里。欲知后面还有什么例子,且听下回分解。