表达式求值,栈的应用(C语言)

无语死了,好在终于弄出来了。


题目:利用栈编写表达式求值程序:输入含有“+”、“-”、“*”、“/”四则运算的表达式,其中负数要用(0-正数)表示,并以=结束。要求输出表达式的值(两运算符号的有限关系见教材《数据结构》表3.1)。


测试样式:

【第一组自测数据】

【键盘输入】

3*(9-7)#↙

【正确输出】

6


【第二组自测数据】

【键盘输入】

(0-12)*((5-3)*3)/(2+2)=

【正确输出】

-18


完整代码

typedef int Status; 
typedef char SElemType;
typedef double SElemType2;
#include"malloc.h" 
#include"stdio.h"
#include"math.h"
#include"stdlib.h"
#include"string.h"
#define OK              1
#define ERROR           0
#define TRUE            1
#define FALSE           0
#define STACK_INIT_SIZE 10 //存储空间初始分配量
#define STACKINCREMENT  2  //存储空间分配增量

///////////////////////////////////////////////////////////////
//gets了字符串后,要将字符串中的【运算符】和【运算数】分开。	   
//故而要建立两个栈,一个存储【运算符】,另一个存储【运算数】。	   
//【运算符栈】为char类型,【运算数栈】为double类型。			   
//因此,相应的,对于两个种同类型的栈,就要有两种不同类型的基本操作。
///////////////////////////////////////////////////////////////

struct SqStack       //运算符型的
{
	SElemType * base;
	SElemType * top;
	int stacksize;
};

struct SqStack2		 //运算数型的
{
	SElemType2 * base;
	SElemType2 * top;
	int stacksize;
};

Status InitStack(SqStack &S)   //运算符栈的初始化操作
{
	S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	if(!S.base) exit(-2);
	S.top = S.base;
	S.stacksize = STACK_INIT_SIZE;
	return OK;
}

Status InitStack2(SqStack2 &S)  //运算数栈的初始化操作
{
	S.base = (SElemType2 *)malloc(STACK_INIT_SIZE * sizeof(SElemType2));
	if(!S.base) exit(-2);
	S.top = S.base;
	S.stacksize = STACK_INIT_SIZE;
	return OK;
}

Status Push(SqStack &S, SElemType e)  //运算符栈的进栈操作
{
	if(S.top - S.base >= S.stacksize)
	{
		S.base = (SElemType *)realloc(S.base,(S.stacksize + STACKINCREMENT) * sizeof (SElemType));
		if(!S.base) exit(-2);
		S.top = S.base + S.stacksize;
		S.stacksize += STACKINCREMENT;
	}
	* S.top = e;
	S.top ++;
	return OK;
}

Status Push2(SqStack2 &S, SElemType2 e)  //运算数栈的进栈操作
{
	if(S.top - S.base >= S.stacksize)
	{
		S.base = (SElemType2 *)realloc(S.base,(S.stacksize + STACKINCREMENT) * sizeof (SElemType2));
		if(!S.base) exit(-2);
		S.top = S.base + S.stacksize;
		S.stacksize += STACKINCREMENT;
	}
	* S.top = e;
	S.top ++;
	return OK;
}

Status In(char c)   //判断是否为运算符,是运算符则返回 TRUE, 否则返回 FALSE
{
	if(c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '=') return TRUE;
	else return FALSE;
}

SElemType GetTop(SqStack S)  //返回运算符栈的栈顶元素
{
	SElemType *p;
	p = S.top;
	p--;
	if(S.base == S.top) return ERROR;
	return(*p);
}

SElemType2 GetTop2(SqStack2 S)  //返回运算数栈的栈顶元素
{
	SElemType2 *p;
	if(S.base == S.top) return ERROR;
	p = S.top;
	p--;
	return(*p);
}

Status Pop(SqStack &S, SElemType &e)  //运算符栈的退栈操作
{
	if(S.top == S.base) return ERROR;
	S.top --;
	e = * S.top;
	return OK;
}

Status Pop2(SqStack2 &S, SElemType2 &e)	//运算数栈的退栈操作
{
	if(S.top == S.base) return ERROR;
	S.top --;
	e = * S.top;
	return OK;
}


//判定运算符栈的栈顶运算符 op1 与读入的运算符 op2 之间有限关系的函数
//这个函数颇蠢,看了别人写的感觉简单明了,不过懒得改了
SElemType Precede(SElemType op1, SElemType op2)	
{												
	switch(op1)
	{
		case '+':if(op2 == '+' || op2 == '-' || op2 == ')' || op2 == '=') return ('>');
				 else return ('<'); break;
		case '-':if(op2 == '+' || op2 == '-' || op2 == ')' || op2 == '=') return ('>');
				 else return ('<'); break;
		case '*':if(op2 == '(') return ('<');
				 else return ('>'); break;
		case '/':if(op2 == '(') return ('<');
				 else return ('>'); break;
		case '(':if(op2 == ')') return ('=');
				 else return ('<'); break;
		case ')':return ('>'); break;
		case '=':if(op2 == '=') return ('=');
			     else return ('<'); break;
	}
}

double Operate(SElemType2 a, SElemType theta, SElemType2 b) //四则运算 a,b为运算数theta 为运算符。
{															//形式为 a theta b
	double an;
	switch(theta)
	{
	case'+':an = a + b; break;
	case'-':an = a - b; break;
	case'*':an = a * b; break;
	case'/':an = a / b; break;
	}
	return(an);   //注意返回值为double型
}

/////////////////////////////////////////////////////////////
//这里要考虑到输入的表达式中有两位数及以上的情况 			  
//如该表达式   12*123=                        			   
//但表达式又是以字符的形式一个一个字符的接收    			   
//即'1','2'是分开的,不是一下子接收'12'         			   
//故而要加一段代码,主要思想为: 				 			   
//在接收到一个数字的时候						 			   
//先暂存起来        					 	     			   
//接收下一个看看是不是还是数字				 			   
//是的话就要和前面这个组合起来							   
//一旦下一个不是,就用atof 将TempData中的字符转为double型数据 
/////////////////////////////////////////////////////////////

SElemType2 EvaluateExpression()
{
	SqStack OPTR;
	SqStack OPND;
	InitStack(OPTR);Push(OPTR, '=');  //其实'='就是书中的'#'。
	InitStack2(OPND);
	SElemType MyExpression[80];	
	SElemType TempData[10];//TempData用来暂存还是字符型时期的“数字”
						   //如3*(9-7)= 中的3,9,7 只是这时还是字符型
	char * p;
	char theta, x;
	double Data, a, b;
	int i = 0;			 //数组TempData的下标
	gets(MyExpression);  //输入表达式
	p = MyExpression;    //令p指向字符型数组MyExpression
	//当读入的字符为'=' 且运算符栈OPTR的栈顶元素为'='是,结束该循环.
	//书中以'#'='#'作为结束的标志,这里以'='='='作为结束的标志
	while(*p != '=' || GetTop(OPTR) != '=')		
	{											
		if(!In(*p))		       //判断p指向的字符是否为运算符
		{					   
			TempData[i] = *p;  //接受到一个数字的时候,暂存入TempData
			p++;			   //接收下一个
			++i;               
			if(In(*p))		          //一旦下一个不是
			{
				TempData[i] = '\0';   //就立即给TempData中暂存的数字最后加上结束符
				Data=atof(TempData);  //atof  功能为:把字符串转换成浮点数  暂存入Data中 
				Push2(OPND, Data); 	  //将Data入栈
				i = 0;				  //归零
			}
		}
		else
			switch (Precede(GetTop(OPTR), *p))					
			{
				case '<':Push(OPTR,*p);	//栈顶元素优先权低
				         p++; break;
				case '=':Pop(OPTR,x);	//脱括号
						 p++; break;
				case '>':Pop(OPTR, theta);//运算符栈的栈顶元素退栈,进行Operate运算,并将运算结果入栈
						 Pop2(OPND, b); Pop2(OPND, a);
						 Push2(OPND, Operate(a, theta, b)); 
						 break;
			}

	}
	return GetTop2(OPND);
}

int main(void)
{
	printf("%g\n",EvaluateExpression());
	return 0;
	
}



运行结果截图:




【小笔记】

        if(!In(*p))            //判断p指向的字符是否为运算符  
        {                        
            TempData[i] = *p;  //接受到一个数字的时候,暂存入TempData  
            p++;               //接收下一个  
            ++i;                 
            if(In(*p))                //一旦下一个不是  
            {  
                TempData[i] = '\0';   //就立即给TempData中暂存的数字最后加上结束符  
                Data=atof(TempData);  //atof  功能为:把字符串转换成浮点数  暂存入Data中   
                Push2(OPND, Data);    //将Data入栈  
                i = 0;                //归零  
            }  
        }  


痛苦死了,起初没写这行。发现过时输入格式为   个位数(运算符)两位数=  时都没错,可一旦反过来  两位数(运算符)个位数 = 就会错。

比如12-3= 结果就等于-20;393-10= 结果就为29。

仔细研究一番,原因实在是太坑爹了。

用393-1= 举例。

gets(MyExpression);

MyExpression =

3
9
3
-
1
=
\0
\0
\0

指针p先指向第一个,即‘3’,然后将其存入TempD中,直到p指向  '-'  ,

则TempData =

3
9
3
\0
\0
\0
\0
\0
\0
\0

然后data = atof(TempData)  即data = 393;

到此时第一个数字393已经转换并且存储结束了。

接下来开始转换字符‘1’了。

但这里就出错了!

p指向1,TempData = 

1
9
3
\0
\0
\0
\0
\0
\0
\0

然后!然后就直接atof了

于是此时data = 193

再接着运算,本来应该393-1的就变成393-193了!



拯救的办法很简单!每当p指完了数字下一个指向运算符时,就紧接着在TempData 已存入的数字后面立即存上一个结束符‘\0’;

此时p应该指向‘=’了,同时TempData =

1
\0
3
\0
\0
\0
\0
\0
\0
\0

于是atof在这种情况下当然只管第一个‘\0’之前的数字了!

自然,Data = 1;


做完这个感觉不错。有时候一些错误真是出人意料啊!把自己的脑子锻炼成编译器那样,大概才能成神吧~!


你可能感兴趣的:(栈,数据结构,表达式求值)