中缀表达式、后缀表达式以及前缀表达式求值详解

后缀表达式

后缀表达式:运算符号位于两个运算数之后。如, a b c + * d e / -

应用堆栈实现后缀表达式求值的基本过程:

从左到右读入后缀表达式的各项(运算符或运算数);

  1. 运算数:入栈;
  2. 运算符:从堆栈中弹出适当数量的运算数,计算并结果入栈;
  3. 最后,堆栈顶上的元素就是表达式的结果值。
    在这里插入图片描述

后缀表达式求值的主体函数

//后缀表达式求值 
double evaluteH(string expression)
{
	stack<double> num;
	double sum=0;
	vector<string> s = scan(expression);//即对象分割
	int size = s.size();
	for(int i=0;i<size;i++)
	{
		if(isdigit(s[i][0])){
			num.push(atof(s[i].c_str()));
		}
		else
		{
			double t2 = num.top();
			num.pop();
			double t1 = num.top();
			num.pop();
			sum = cal(t1,t2,s[i][0]);
			num.push(sum);
		}
	}
	return sum;
}

其它辅助函数

对象分割函数scan()
//经过扫描,scan将输入的字符串中操作数和操作符进行了分离 
//即抽取expression中的数字、运算符和括号,将抽取结构储存在字符串向量v中 
//例如,expression:(13+2)*4-3;则返回结果为:(,13,+,2,),*,4,-,3
vector<string> scan(string a)
{
   	vector<string> v;//用string类型向量v来储存处理后 数字、运算符和括号,
	                 //这样巧用string ,会使后续运算更加方便
	string numString;//用来储存数字 
	int len = a.size();
	for(int i=0;i<len;i++)//逐个扫描 
	{
		if(isdigit(a[i]) || a[i] == '.')//如果是数字或小数点,存入numString中 
		{
			numString.append(1,a[i]);
		}
		else
		{
			if(numString.size() >0 )
			{
				v.push_back(numString);
				//如果不是数字说明,numString已储存一个完整的操作数一个操作数
				//把记录的数字压入v中 
				numString.erase();//更新numString 
			}
			if(!isspace(a[i]))//不是空格符,就是运算符 
			{
				string s;
				s.append(1,a[i]);
				v.push_back(s);//把运算符推入v中 
							}
		}
	 } 
	 if(numString.size() > 0)
	 {v.push_back(numString);	  } //把最后一个数字压入v中
	 return v; 
} 

进行一次计算的函数cal()
double cal(double a,double b,char op)
{
	switch  (op)
	{
		case '+':return a + b;
		case '-':return a - b;
		case '*':return a * b;
		case '/':return a / b;
		case '%':return (int)a % (int)b;
		case '^':return pow((int)a,(int)b) ;
		default:cout << "error !";
	}
}

中缀表达式

中缀表达式:运算符号位于两个运算数之间。如 ,a + b * c - d / e

求值思路:

转换为后缀表达式求值:
在这里插入图片描述
在这里插入图片描述

思路一——转换完再求解

将中缀表达式的字符序列转换为后缀表达式的字符序列,再配合本文上面后缀表达式的求值函数进行求值。

转换函数string change(string expression);如下:
//将中缀表达式的字符序列转换为后缀表达式 
string change(string expression)
{  
    string v="";
    string numString;//为使输出的两操作数之间有空格,用numString分割每一个单独的操作数 
    stack<char> op;//操作符临时栈 
	 
    int len = expression.length();
	for(int i=0;i<len;i++)
	{  
		if(isdigit(expression[i]) || expression[i] == '.'|| isalpha(expression[i]) )
		{
			numString.append(1,expression[i]);
		}
		else
		{
			if(numString.size() > 0)
			{
				
				v = v + numString  + " ";//加一个空格 
				numString.erase();
			}
		}
			//特别注意,括号不能出现在后缀表达式中 
		if(!isspace(expression[i])&&!isdigit(expression[i])&&!isalpha(expression[i]))
		{    if(!op.empty())
		     {  char op1 = op.top();
			    char op2 = expression[i];
		     	if(op2 == ')')
		     	{
		     		while(op1 != '(')
		     		{
		     			v.append(1,op1);
		     			v += " ";
		     			op.pop();
		     			op1 = op.top();
					 }
					 op.pop(); 
				 }
				 else
				 {
				 	while(!op.empty() &&priority(expression[i])  <= priority(op.top())&&op.top()!='(')
			       {  			
					v.append(1,op.top());
				    v += " ";				
					op.pop();	
								
				   }
				   op.push(expression[i]);
				 }
			   } 
		 else
			op.push(expression[i]);	
		}
	}
	//别忘了清楚最后留在numString和操作数栈中的操作数或操作符
	if(numString.size() > 0)
	{				
		v = v + numString  + " ";//加一个空格 
		numString.erase();
	} 
	while(!op.empty())
	{   if(op.top() != '(')
		{
			v.append(1,op.top());
		v += " ";
		}
		op.pop();		
	}
	return v;
 } 
优先级函数----int priority(char op);

其中优先级比较运用了int priority(char op);函数

//返回各符号的优先级,用数字表示,便于判断 
int priority(char op)
{
	if(op == ')')
	return 0;
	else if(op == '+'||op == '-')
	return 1;
	else if(op == '*'||op == '%'||op == '/')
	return 2;
	else if(op == '^')
	return 3;
	else if(op == '(')
	return 4;
	else
	{
		cout << "error";
		return -1;
	}
}

思路二——边转化边求解

这里的符号优先级是用多重暴力讨论来解决,也并不复杂

主要函数
double evalute1(string expression)
{
	stack<double> num;
	stack<char> op;
	vector<string> s = scan(expression);
	//经过扫描,s将输入的字符串中操作数和操作符进行了分离 
	//即抽取expression中的数字、运算符和括号,将抽取结构储存在字符串向量s中 
	//例如,expression:(13+2)*4-3;则s为:(,13,+,2,),*,4,-,3
	int size = s.size();
	for(int i=0;i<size;i++)
	{
		if(s[i][0] == '+' || s[i][0] == '-')
		{
			while(!op.empty() && op.top() != '(')
			{
				cal(num,op);
			}
			op.push(s[i][0]);
		}
		else if(s[i][0] == '*' ||s[i][0] == '/'||s[i][0]=='%')
		{
			while(!op.empty() && (op.top() == '%'  ||op.top() == '*'  || op.top() == '/'||op.top() == '^'))
			{
				cal(num,op);
			}
			op.push(s[i][0]);
		}else if(s[i][0] == '^'){
			op.push(s[i][0]);
		}
		else if(s[i][0] == '('){
			op.push(s[i][0]);
		}else if(s[i][0] == ')')
		{
			while(op.top() != '(')
			{
				cal(num,op);
			}
			op.pop();
		}else
		{
		  num.push(atof(s[i].c_str()))	;//调用c_str,atof()将字符串转浮点型数字, 
		}
	}
		while(!op.empty()){
			cal(num,op);	}
		return num.top();
	
} 
scan()扫描分离函数同上所示
//经过扫描,scan将输入的字符串中操作数和操作符进行了分离 
//即抽取expression中的数字、运算符和括号,将抽取结构储存在字符串向量v中 
//例如,expression:(13+2)*4-3;则返回结果为:(,13,+,2,),*,4,-,3
vector<string> scan(string a)
{
   	vector<string> v;//用string类型向量v来储存处理后 数字、运算符和括号,
	                 //这样巧用string ,会使后续运算更加方便
	string numString;//用来储存数字 
	int len = a.size();
	for(int i=0;i<len;i++)//逐个扫描 
	{
		if(isdigit(a[i]) || a[i] == '.')//如果是数字或小数点,存入numString中 
		{
			numString.append(1,a[i]);
		}
		else
		{
			if(numString.size() >0 )
			{
				v.push_back(numString);
				//如果不是数字说明,numString已储存一个完整的操作数一个操作数
				//把记录的数字压入v中 
				numString.erase();//更新numString 
			}
			if(!isspace(a[i]))//不是空格符,就是运算符 
			{
				string s;
				s.append(1,a[i]);
				v.push_back(s);//把运算符推入v中 
							}
		}
	 } 
	 if(numString.size() > 0)
	 {v.push_back(numString);	  } //把最后一个数字压入v中
	 return v; 
} 



计算函数cal()则有所不同
//在操作符栈中取栈顶元素,在操作数栈中取头两个元素,
//然后计算,并将计算结果推入操作数栈 
double cal(stack<double>& num,stack<char>& op)//要传引用,否则会出错 
{
	char ch = op.top();
	op.pop();
	double b = num.top();
	num.pop();
	double a = num.top();
	num.pop();
	switch  (ch)
	{
		case '+':num.push(a + b);return a + b;
		case '-':num.push(a - b);return a - b;
		case '*':num.push(a * b);return a * b;
		case '/':num.push( a / b);return a / b;
		case '%':num.push(fmod(a,b));return fmod(a,b);//取余操作,调用fmod()实现浮点数取余 
		case '^':num.push(pow((int)a,(int)b));return pow((int)a,(int)b) ;
		default:cout << "error !";
	}
}

测试结果如下所示

支持整数浮点数,加减乘除,取余,乘方运算
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

啃完了后缀,中缀表达式,最后奉上前缀表达式的求值方式放松放松

前缀表达式

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




逆波兰表达式的定义:
1)一个数是一个逆波兰表达式,值为该数
2)"运算符 逆波兰表达式 逆波兰表达式”是逆波兰表达式,值为两个逆波兰表达式的值运算的结果

思路

不需要考虑运算优先级,用递归,直接上代码

//读入一个逆波兰表达式,并计算其值 
double exp()
{
 char s[20];
 cin >> s;
 switch (s[0])
 {
  case '+' :return exp() + exp();
  case '-' :return exp() - exp();
  case '*' :return exp() * exp();
  case '/' :return exp() / exp();
  default  :return atof(s);//不是运算符,就是数,所以将s转化为数字 
 }
}
int main(){
 cout<<exp();
} 
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值