用栈实现表达式求值

以前文章中写过一个表达式求值,但是逻辑有点乱,所以重新写了一个,该程序目前只支持个位数内的加减乘除,和以前的实现思想不一样,采用的是后缀表达式的思想,不知道的可以到网上去查查资料。觉得这样的思想,更容易理解。

通过这个程序让我收获挺大,由于需要创建两个栈,一个是数据栈,另一个是字符栈。

方案1:思想是:由于我们只支持个位数以内的加减乘除,所以对于操作数就可以使用字符表示,那么对于数据栈也可以使用字符栈来表示。

方案2:写两个栈,代码都得复制一遍,然后是修改,这样重复代码很高,这也就是引入多态的思想了,在C++/java中有多态泛型,但是C中没有,那么可以用C来实现多态,一个方案就是函数指针,另一个方案便是使用宏。本程序使用宏来实现多态。

#ifndef STACK_ELEMENT_TYPE
#error "使用本文件前,必须先定义STACK_ELEMENT_TYPE"
#endif

#ifndef STACK_TYPE
#error "使用本文件前,必须先定义STACK_TYPE"
#endif

#include 

typedef struct
{
    STACK_ELEMENT_TYPE* elements;
    int top;
    int max;
} STACK_TYPE;

int temp;
/* 注意:
 * 1. 每个操作的宏,都要用#ifndef扩起,否则如果多次使用这个文件,编译器就会报告告警甚至错误
 * 2. C语言的malloc返回值是void*,可以直接赋值给(stack)->elements。但是C++不行,因为C++的类型检查更加严格。
 *    C++可以在STACK_INIT这个宏里面再加入一个参数来表示类型,但是不推荐。C++推荐直接用模板。
 **/
#ifndef STACK_INIT
#define STACK_INIT(stack, maxSize) \
    ((stack).elements = malloc(maxSize * sizeof(((stack).elements)[0])), (stack).top = 0, (stack).max = maxSize)
#endif

#ifndef STACK_DESTROY
#define STACK_DESTROY(stack) \
    (free((stack).elements), (stack).elements = NULL, (stack).top = 0, (stack).max = 0)
#endif

#ifndef STACK_PUSH
#define STACK_PUSH(stack, value) \
    (((stack).elements)[(stack).top++] = (value))
#endif

#ifndef STACK_POP
#define STACK_POP(stack, value) \
    ((value) = ((stack).elements)[--(stack).top])
#endif


#ifndef STACK_TOP
#define STACK_TOP(stack, value) \
   ( (temp)=  (stack).top  ,((value) = ((stack).elements)[--temp])  )
#endif

//EvalExpression.c

#include

#define STACK_ELEMENT_TYPE  char
#define STACK_TYPE          CharStack
#include "stack_template.h"
#undef STACK_ELEMENT_TYPE
#undef STACK_TYPE

#define STACK_ELEMENT_TYPE  int
#define STACK_TYPE          IntStack
#include "stack_template.h"
#undef STACK_ELEMENT_TYPE
#undef STACK_TYPE

typedef enum{
	lparen,rparen,plus,minus,times,divide,eos,operand
} precedence;


//函数列表
int  precede(char stacktop,char op);
void Postfix(const char *expr , char output[] );
int Eval(char expr[]);
precedence GetToken(char expr[], char * symbol ,int *n);

//由中缀表达式计算结果
int Eval(char postfix[])
{

	char symbol;
	int op1,op2;
	int n=0;
	precedence token=GetToken(postfix,&symbol ,&n);
	IntStack intstack;
	STACK_INIT(intstack, 20);
	while(token !=eos)
	{
		if(token==operand)
			STACK_PUSH(intstack, symbol-'0');
		else
		{
			STACK_POP(intstack, op2);
			STACK_POP(intstack, op1);
			switch(token)
			{
				case plus:  STACK_PUSH(intstack, op1+op2 );break;
				case minus: STACK_PUSH(intstack, op1-op2 );break;
				case times: STACK_PUSH(intstack, op1*op2 );break;
				case divide:STACK_PUSH(intstack, op1/op2);break;
			}
		}
		token=GetToken(postfix,&symbol,&n);
	}
	int result=0;
	STACK_POP(intstack, result);
	STACK_DESTROY(intstack);
	return result;
}

precedence GetToken(char expr[], char * symbol ,int *n)
{
	*symbol=expr[ (*n)++ ];
	switch( *symbol)
	{
		case '(': return lparen;
		case ')': return rparen;
		case '+': return plus;
		case '-': return minus;
		case '*': return times;
		case '/': return divide;
				  //case '%': return mod;
		case '\0': return eos;
		default : return operand;
	}
}


//把中缀表达式转为后缀表达式
//不过该方法只能处理10以内的数的加减乘除,其实也可以只用一个字符栈来实现,我们输入的数据是字符串,
//所以数字也是字符表示的,这样就不必使栈支持多种数据类型.
//不过通过这个可以看出要实现多态可以采用函数指针,还有本程序采用的宏实现,或者C++里面的模板类.

/*
 * 也可以不采用后缀表达式的方法实现多位数的计算。用后缀表达式不能实现多位数的运算,因为一个字符最多为255。
 */
void Postfix(const char *expr , char output[] )
{
	if(!expr)
		printf("the expression is null\n");
	char symbol;
	int i=0;
	CharStack charstack;
	STACK_INIT(charstack, 10);
	STACK_PUSH(charstack, '#');//刚刚开始字符栈里需要一个优先级最低的字符,这样才可以比较,否则栈为空无法比较。
	for( ;*expr!='\0';expr++)
	{
		if(*expr>='0'&& *expr <='9' )
			output[i++]=*expr;

		else if(*expr==')')
		{
			while(1)
			{
				STACK_POP(charstack, symbol);
				if( symbol!='(')
					output[i++]=symbol;
				else
					break;
			}
		}
		else //此时为操作符或者lparen
		{
			char stacktop;
			STACK_TOP(charstack, stacktop);
			if( precede(stacktop, *expr)<0 )                         
				STACK_PUSH(charstack, *expr);
			else
				output[i++]=*expr;
		}

	}
	while(1) //不需要用栈判空函数,因为刚开始时默认把#字符入栈了。正好就不需要写该函数了
	{
		STACK_POP(charstack, symbol);
		if( symbol!='#')
			output[i++]=symbol;
		else
			break;
	}
	output[i]='\0';
	STACK_DESTROY(charstack);
}


//比较2个操作符的优先级
int  precede(char stacktop,char op)
{
	int i,j;
	int prior[7][7]={
		{ 1, 1,-1,-1,-1, 1, 1},
		{ 1, 1,-1,-1,-1, 1, 1},
		{ 1, 1, 1, 1,-1, 1, 1},
		{ 1, 1, 1, 1,-1, 1, 1},
		{-1,-1,-1,-1,-1, 0, 2},
		{ 1, 1, 1, 1, 2, 1, 1},
		{-1,-1,-1,-1,-1, 2, 0}};

	switch(stacktop)
	{
		case '+':i=0;break;
		case '-':i=1;break;
		case '*':i=2;break;
		case '/':i=3;break;
		case '(':i=4;break;
		case ')':i=5;break;
		case '#':i=6;break; 
	}
	switch(op){
		case '+':j=0;break;
		case '-':j=1;break;
		case '*':j=2;break;
		case '/':j=3;break;
		case '(':j=4;break;
		case ')':j=5;break;
		case '#':j=6;break; 
	}   

	return prior[i][j];
}

int main()
{
	char expr[20]="(1*(2+3)*4)+1";
	char output[20];
	//scanf("%s",expr);
        printf("中缀表达式为:%s\n",expr);
	Postfix(expr,output);
	printf("后缀表达式为:%s\n",output);
	int result=Eval(output);
	printf("计算结果为:%d\n",result);
	return;
}


你可能感兴趣的:(C,Data,Struct,Algorithm)