数据结构(5)栈和队列->栈的应用之表达式求值

  • 整理数据结构学习笔记
  • 基于C++

一、表达式的分类

1、中缀表达式

(1)中缀表达式就是常见的运算表达式,如(3+4)×5-6

(2)中缀表达式的计算机求值方法:

  • 先在表达式前后加上起止符#(其运算优先级最低)
  • 建立两个辅助栈,一个运算数栈和一个运算符栈
  • 从左至右遍历表达式,如果遇到数字直接压入数字栈;如果遇到算符,若符号栈空则直接压入,否则与符号栈顶元素比较,若栈顶元素优先级低则入栈,若优先级一样则栈顶元素出栈与算符抵消(指针后移跳过当前算符),若栈顶元素优先级高,则弹出两个数字一个符号进行运算(次顶元素 op 栈顶元素),运算结果压入数字栈,再处理当前算符。最后留在数字栈中的数即为结果
  • 重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果

(3)运算符优先级如下表(如第4行代表*的优先级低于(,高于所有其他符号,#是起止符)
在这里插入图片描述
(4)看一个例子,应该好理解一点,如下计算3*(7-2),OPTR=运算符栈,OPND=运算数栈
在这里插入图片描述

2、前缀表达式

(1)前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前,如(3+4)*5-6转化为前缀表达式为- x + 3 4 5 6
(2)前缀表达式的计算机求值方法:

  • 建立一个运算数辅助栈
  • 从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
  • 重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果

(3)看一个例子,应该好理解一点,如下计算1+2*(6-4)-12/4,转为-+1*2-6 4/12 4

在这里插入图片描述
在这里插入图片描述

3、后缀表达式

(1)后缀表达式又称逆波兰表达式运算符位于操作数之后,例如(3+4)*5-6转化为后缀表达式为3 4 + 5 × 6 -
(2)后缀表达式的计算机求值方法:

  • 建立一个运算数辅助栈
  • 从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 op 栈顶元素),并将结果入栈
  • 重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果

(3)看一个例子,应该好理解一点,如下计算1+2*(6-4)-12/4,转为1 2 6 4-*12 4/-+
在这里插入图片描述

二、表达式的互相转化

有三种方法可以实现中缀转前后缀的表达式转化

  • 利用表达式树
  • 利用辅助栈
  • 简便方法(加括号法 )

可以参考这两篇文章:

三、中缀表达式计算代码(部分)

数据结构

使用两个栈,用动态申请数组实现,如下:

#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10

//+ - * / ( ) #
enum sign{add,sub,mul,divis,Lbracket,Rbracket,SE};
enum cmp{s=-1,e,b,err};
//优先级比较矩阵 
cmp CM[7][7]={b,b,s,s,s,b,b,
			  b,b,s,s,s,b,b,
			  b,b,b,b,s,b,b,
			  b,b,b,b,s,b,b,
			  s,s,s,s,s,e,err,
			  b,b,b,b,err,b,b,
			  s,s,s,s,s,err,s};
//数字栈 
typedef struct
{
	float *base;
	float *top;
	int stacksize;
}NumStack;
//符号栈 
typedef struct
{
	char *base;
	char *top;
	int stacksize;
}SignStack;

主循环

//算数符号转换为枚举类型,便于查找优先级矩阵 
sign char2sign(char a)
{
	sign res;
	switch(a)
	{
		case '+':res=add;break;
		case '-':res=sub;break;
		case '*':res=mul;break;
		case '/':res=divis;break;
		case '(':res=Lbracket;break;
		case ')':res=Rbracket;break;
		case '#':res=SE;break;
	}
	return res;
}

//符号a和b相比优先级情况 
int Sign_Cmp(char a,char b)
{
	sign A=char2sign(a);
	sign B=char2sign(b);
	
	return CM[A][B]; 
}
//进行一次四则运算 
float cal(char sign,float a,float b)
{
	switch(sign)
	{
		case '+':return a+b;
		case '-':return a-b;
		case '*':return a*b;
		case '/':return a/b;
	}
}

int main()
{
	char equation[100];
	NumStack NS;
	SignStack SS;
	NumStack_Init(NS);
	SignStack_Init(SS);	
	
	char *p=&equation[0];
	char sign;
	float num=0;
	int init=1;
	int flag=1;//辅助从字符串转换小数 
	int temp=10;//辅助从字符串转换小数 
	
	while(1)
	{
		//指针重定位
		p=&equation[0];
		//算式输入,添加起止符# 
		equation[0]='#';
		scanf("%s",&equation[1]);
		int len=strlen(equation);
		equation[len]='#';
		equation[len+1]='\0';
		//主循环,分析&计算 
		while(*p!='\0')
		{	
			while((*p<'0' || *p>'9') && *p!='\0')
			{
//				NumStack_show(NS);	//for debug
//				SignStack_show(SS);	//for debug
				
				if(SignStack_Empty(SS))
					SignStack_push(SS,*p),p++;
				else
				{
					SignStack_GetTop(SS,sign);
					
					if(Sign_Cmp(sign,*p)==s)		//栈中的比新来的优先级低,入栈
						SignStack_push(SS,*p),p++;
					else if(Sign_Cmp(sign,*p)==e)	//栈中的和新来的优先级一样,出栈抵消 
						SignStack_pop(SS,sign),p++;	 
					else if(Sign_Cmp(sign,*p)==b)	//栈中的比新来的优先级高,弹出一个符号两个数进行计算,结果入数字栈 
					{
						float t1,t2;
						NumStack_pop(NS,t2);
						NumStack_pop(NS,t1);
						SignStack_pop(SS,sign);
						num=cal(sign,t1,t2);
						NumStack_push(NS,num);
					}
				}
			}
			

			while(((*p>='0' && *p<='9') || *p=='.') && *p!='\0')
			{
				if(init)
				{
					num=0;
					temp=10;
					flag=1;
					init=0;
				}
				
				if(*p!='.')
				{
					if(flag)//分离整数部分 
						num=num*10+(*p-'0'),p++;
					else	//分离小数部分 
					{
						num+=1.0*(*p-'0')/temp;
						temp*=10;
						p++;
					}
				} 
				else if(*p=='.')
					p++,flag=0;	
			}
			init=1;
			NumStack_push(NS,num);
		}
		
		SignStack_Clear(SS);	//清空栈,准备下一轮 
		NumStack_Clear(NS);
		NumStack_pop(NS,num);	//本轮运算结果 
		cout<<num<<endl;
	} 
	return 0;
}

运行结果

在这里插入图片描述

完整程序下载

表达式计算

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值