将算数表达式转换成后缀表达式并计算结果

背景    

 

  用计算机实现带括号的四则运算的方式。这里的困难在于乘除运算的优先级高于加减运算,并且加入了括号,使得问题变得更加困难。20世纪50年代,波兰逻辑学家想到了一种不需要括号的后缀表达法,我们也把它称为逆波兰表示。比如:9+(3-1)*3+10/2,如果用后缀表示法就是9 3 1 - 3 * + 10 2 / +,这样的表达式称为后缀表达式,叫后缀的原因在于所有的符号都是要在运算数字的后面出现。

  假设算数表达式只包含“+”、“-”、“×”、“÷”、正整数和括号的合法数学表达式,这称为简单算数表达式。对该表达式求值的过程是:先将算数表达式转换成后缀表达式(亦称为逆波兰表达式),然后对该后缀表达式求值。

  为什么要这样做呢?人可以通过看一眼就知道先乘除后加减进行计算,但是计算机不可以。如果转换成后缀表达式,就已经把运算次序考虑过了,计算机在进行运算的时候就不需要考虑运算的次序。

建议

  本篇博客大概有170行代码,如果逐行阅读,会有一种云里雾里的感觉,建议先去体会如何利用后缀表达式求值,可以在演草纸上写写画画,理解了使用后缀表达式相对算数表达式(中缀表达式)的好处之后,再仔细揣摩后缀表达式是如何得到的。代码量一旦增加,阅读他人的代码,就要由顶至下去阅读,逐行阅读费时费力,而且不是很简单读懂他人的代码。

#include
#include
#define MaxSize 100

  

  宏定义NUL为字符串结束符:

#define NUL '\0'

  

  运算符栈用于存放后缀表达式(重点):

/*运算符栈类型*/
typedef struct
{
	char data[MaxSize];
	int top;
}OP;

  

  数值栈用于存放数值

/*数值栈类型*/
typedef struct
{
	float data[MaxSize];
	int top;
}ST;

 

trans函数编写思路如下:遍历字符串exp

 

  • 字符ch为数字,将后继所有数字字符均依次存放到postexp中,并以字符"#"标志数字串的结束
  • 字符ch为左括号"(",将此括号进栈到运算符栈op中
  • 字符ch为右括号")",将运算符栈op中左括号"("以前的运算符依次出栈,并将左括号删除
  • 字符ch的运算符优先级不大于运算符栈op的栈顶运算符(除了栈顶运算符为左括号“(”外)的优先级,则依次出栈并存入到postexp中,然后将字符ch进栈
  • exp扫描完毕,将运算符栈op中所有的运算符依次出栈并存放到postexp中,得到后缀表达式postexp
/*****************************************************************
*函数名:trans
*函数功能描述:将中缀表达式exp转化为后缀表达式postexp(也称逆波兰表达式)
*函数参数:exp[]-存放中缀表达式的字符数组,postexp[]-存放后缀表达式的字符数组
*函数返回值:无返回值
*作者:王赋睿
*函数创建日期:2018.5.27
*函数修改日期:尚未修改
*修改人:尚未修改
*修改原因:尚未修改
*版本:1.0
*历史版本:无
*****************************************************************/
void trans(char exp[], char postexp[])
{
	char ch;
	int i = 0, j = 0;
	OP op;
	op.top = -1;//初始化运算符栈

	ch = exp[i]; i++;
	while (ch != NUL)
	{
		switch (ch)
		{
		case '(':
			op.top++;
			op.data[op.top] = ch;
			break;
		case ')':
			while (op.data[op.top] != '(')
			{
				postexp[j++] = op.data[op.top--];
			}
			op.top--;
			break;
		case '+':
		case '-':
			while (op.top != -1 && op.data[op.top] != '(')
			{
				postexp[j++] = op.data[op.top--];
			}
			op.top++; op.data[op.top] = ch;
			break;
		case '*':
		case '/':
			while (op.top != -1 && op.data[op.top] != '(' && (op.data[op.top] == '*' || op.data[op.top] == '/'))
			{
				postexp[j++] = op.data[op.top--];
			}
			op.top++;
			op.data[op.top] = ch;
			break;
		case ' ':
			break;//过滤掉空格
		default:
			while (ch >= '0'&&ch <= '9')
			{
				postexp[j++] = ch;
				ch = exp[i++];
			}
			i--;
			postexp[j++] = '#';
			break;
		}
		ch = exp[i++];
	}

	while (op.top != -1)
	{
		postexp[j++] = op.data[op.top--];
	}

	postexp[j] = NUL;
}

  

  针对后缀表达式进行求值:遍历后缀表达式postexp:

 

  • 字符ch为数字,将后继的所有数字字符构成一个整数存放到数值栈st中
  • 字符ch为"+"或"-",则从数值栈st中退栈两个运算数,相加(减)后进栈数值栈st中
  • 字符ch为"×",则从数值栈st中退栈两个运算数,相乘后进栈数值栈st中
  • 字符ch为"÷",则从数值栈退栈两个运算数,相除后进栈st中(若除数为0,则提示错误)
/*****************************************************************
*函数名:compvalue
*函数功能描述:后缀表达式postexp(也称逆波兰表达式)的值计算出来
*函数参数:postexp[]-存放后缀表达式的字符数组
*函数返回值:返回值是计算出来的值
*作者:王赋睿
*函数创建日期:2018.5.27
*函数修改日期:尚未修改
*修改人:尚未修改
*修改原因:尚未修改
*版本:1.0
*历史版本:无
*****************************************************************/
float compvalue(char postexp[])
{
	float d=0;
	char ch;
	int i = 0;
	ST st;
	
	st.top = -1;
	ch = postexp[i++];
	while (ch != NUL)
	{
		switch (ch)
		{
		case '+':   //从数值栈st退栈两个运算数,相加后进入数值栈st中
			st.data[st.top - 1] += st.data[st.top];
			st.top--;
			break;
		case '-':   //从数值栈st退栈两个运算数,相减后进入数值栈st中
			st.data[st.top - 1] -= st.data[st.top];
			st.top--;
			break;
		case '*':   //从数值栈st退栈两个运算数,相乘后进入数值栈st中
			st.data[st.top - 1] *= st.data[st.top];
			st.top--;
			break;
		case '/':   //从数值栈st退栈两个运算数,相除后进入数值栈st中
			if (st.data[st.top] != 0)
				st.data[st.top - 1] /= st.data[st.top];
			else
			{
				printf("\n\t除零错误!\n");
				exit(0);
			}
			st.top--; 
			break;
		default:
			d = 0;     //将数字字符转换成对应的数值存放在d中				
			while (ch >= '0'&&ch <= '9')	//判定为数字字符
			{
				d = 10 * d + ch - '0';
				ch = postexp[i++];
			}
			st.top++;
			st.data[st.top] = d;
			break;
		}
		ch = postexp[i++];
	}

	return st.data[st.top];
}

  

  进行测试用例:

int main()
{
	char exp[MaxSize] = { "15*66-89*13+10" };
	char postexp[MaxSize];
	trans(exp, postexp);
	float d = compvalue(postexp);
	printf("%f", d);
	return 0;
}

将算数表达式转换成后缀表达式并计算结果_第1张图片

本程序在VS2017下运行通过

你可能感兴趣的:(将算数表达式转换成后缀表达式并计算结果)