Skip to content

Latest commit

 

History

History
324 lines (311 loc) · 10.3 KB

File metadata and controls

324 lines (311 loc) · 10.3 KB

一起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	}

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

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