你航数据结构第四次作业——计算器(表达式计算-后缀表达式实现,结果为浮点)

【问题描述】

从标准输入中读入一个算术运算表达式,如:24 / ( 1 + 5/3 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 )= ,计算表达式结果,并输出。

要求:

1、表达式运算符只有+、-、*、/,表达式末尾的=字符表示表达式输入结束,表达式中可能会出现空格;
2、表达式中会出现圆括号,括号可能嵌套,不会出现错误的表达式;

3、表达式中出现的操作数都是十进制整数常量;但要求运算结果为浮点型,例如:5/2结果应为2.5。

4、要求采用逆波兰表达式来实现表达式计算。

【输入形式】

从键盘输入一个以=结尾的算术运算表达式。操作符和操作数之间可以有空格分隔。

【输出形式】

在屏幕上输出计算结果,小数点后保留两位有效数字

【样例输入】

24 / ( 1 + 5/3 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 )     =

【样例输出】

19.64

我在做本题的时候,思路如下:
因为本人脑回路清奇,打算先存再运算,那么就遇到了以下问题,用到了一种不常用的就够,就权当给各位大牛乐呵一下。
首先,面对的一个最基本、最关键的问题就是选用什么样的数据结构。
毋庸置疑的是肯定用栈结构,又因为之后涉及到表达式计算,需要频繁挪动栈中元素的位置,使用双向链接栈非常不方便,故采用静态数组结构。
不用多想,符号栈肯定就只是一个字符串。而对后缀表达式的数据结构的选择我认为才是本题的关键。
我一开始考虑的是全用字符串存每一个数据,即把数字、运算符都当作字符串存起来。也就是把结构体或二维数组当作后缀表达式。
但这样虽然前期在读入的过程中可以正确存入,但在后续运算过程中,必须把数字从字符串类型转化为浮点型才能运算,但转化为浮点型后的数据就没地方存了。如果另开一个数组存放,会破坏掉原有的顺序(也就是说,另开一个数组存入,你不知道这个数据的下一个数据按原来的顺序是哪个),而无法进行之后的操作。
因此,我就想能不能把字符和浮点型都用一个变量来存,想让它是什么就是什么,说白了就是a[1]既可以是char类型,也可以是int类型。答案就是联合(union)。
但是我们都知道,联合虽然可以“任意”该改变其数据类型,但每次存入与之后的读取或者运算要一致,因此,对每一个联合都需要记录其当前的数据类型。
最后选定的数据类型就是结构体数组,其中每个元素有一个float和一个union。唯一无法避免的问题就是不知道输入的表达式会有多长,只能将数组开大一点。

#include 
#include 
typedef struct {
	int sign;
	union{
		double data;
		char op;
	}u;
}node;
int Index(int m)
{
	if(m==1)
	{
		return 1;
	}
	else
	{
		return 10*Index(--m);
	}
}
double get_number(char s[])
{
	int len,num=0,i=0;
	len=strlen(s);
	while(len>0)
	{
		num=num+(s[i]-'0')*Index(len);
		//易错:字符与数组运算时要进行数据类型转换,千万别忘了,否则不会报错,会按其ASC码运算。 
		i++;
		len--;
	} 
	return num*1.0;
}
void sinto(char s[],node *suffix)
{
	(*suffix).sign=1;
	(*suffix).u.data=get_number(s);
}
void cinto(char s,node *suffix)
{
	(*suffix).sign=2;
	(*suffix).u.op=s;
}
int compare(char ch,char op)
{
	if(((ch=='*'||ch=='/')&&(op=='+'||op=='-'))||(op=='('))
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
double cal(double a,double b,char ch)
{
	if(ch=='+')
	{
		return (a+b)*1.0;
	}
	else if(ch=='-')
	{
		return (a-b)*1.0;
	}
	else if(ch=='*')
	{
		return (a*b)*1.0;
	}
	else 
	{
		return (a/b)*1.0;
	}
	//最好多乘个1.0,可以防止某些隐式转换,需要浮点数的时候就不用记那么多,直接乘1.0就行 
}
int main()
{
	node suffix[2000];//后缀表达式栈 
	char op[200];//符号栈 
	int s_top=-1,op_top=-1,out=0,go_on=1; //要初始化go_on为1 
	char temp[50],ch;
	while(1)
	{//在输入过程中转化为后缀表达式 
		if(go_on==1)
		{
			scanf("%c",&ch);
		}
		if(ch==' ')
		{//跳过空格 
			go_on=1;
			continue;
		} 
		else if(ch=='=')
		{//等号直接跳出循环 
			break;
		}
		else if(ch>='0'&&ch<='9')
		{//数字
			int i=0;//i为数字读入时字符串下标
			go_on=1;
			temp[i++]=ch;
			while(1)
			{
				scanf("%c",&ch);
				if(ch==' ')
				{//说明数字已经读完了,此时go_on还应为1,因为这个是空格。 
					//入栈
					temp[i]='\0';
					sinto(temp,&suffix[++s_top]);
					break;
				} 
				else if(ch=='=')
				{
					out=1;//标记以退出最外层循环 
					//入栈
					temp[i]='\0';
					sinto(temp,&suffix[++s_top]);
					break;//退出循环 
				}
				else if(ch<'0'||ch>'9')
				{
					go_on=0;//当go_on为0时,说明读入了一个不是数字的字符,下一次不用再读入 
					//入栈
					temp[i]='\0';
					sinto(temp,&suffix[++s_top]);
					break;
				}
				else if(ch>='0'&&ch<='9')
				{
					temp[i++]=ch;
				}
			}
			if(out==1)
			{
				break;
			}
		}
		else if(ch=='(')
		{//左括号,入符号栈 
			go_on=1;
			op[++op_top]='(';
		} 
		else if(ch==')')
		{//右括号,将前面的符号出符号栈,入后缀表达式栈 
			go_on=1;
			while(op[op_top]!='(')
			{
				cinto(op[op_top--],&suffix[++s_top]);	
			}
			op_top--;//左括号出栈 
		} 
		else
		{//运算符,比较判断是否入符号栈 
			int finish=0;//finish表示这个运算符是否入栈 
			go_on=1;
			while(finish==0)
			{
				if(op_top==-1)
				{//符号栈为空 
					op[++op_top]=ch;
					finish=1; 
				} 
				else if(compare(ch,op[op_top]))
				{//ch优先级高于op[op_top],ch入栈 
					op[++op_top]=ch; 
					finish=1;
				}
				else
				{//ch优先级=0)
		{
			cinto(op[op_top--],&suffix[++s_top]);
		}
	}
	//至此,后缀表达式转换完成。
	int i=2,tempr;//注意i要初始化为2,因为运算符最早只会出现在第二位。 
	while(i<=s_top)
	{//为什么i要大于等于1呢,是因为每次i是位于运算后的结果的位置。 
		if(suffix[i].sign==2)
		{
			//i-1与i-2的data按i的运算符运算,并存入i-2 
			suffix[i-2].u.data=cal(suffix[i-2].u.data,suffix[i-1].u.data,suffix[i].u.op)*1.0;
			tempr=i+1;
			//向前依次挪两位 
			while(tempr<=s_top)
			{
				suffix[tempr-2]=suffix[tempr];
				tempr++;
			}
			i=i-2; 
			s_top=s_top-2; 
		}
		else
		{
			i++;
		}
	}
	printf("%.2f",suffix[0].u.data);
	return 0;
} 

最后,说一下正常思路。对于表达式的计算问题,可以边读入边计算,也就是符号出栈的时候就可以运算了,因为一旦某个符号出栈意味着它在后缀表达式中的位置及它之前的元素都已经确定了(这是这种思路的关键),这种思路只用开两个栈,不用结构体套联合。

你可能感兴趣的:(数据结构,算法,c语言)