栈应用:中缀表达式,后缀表达式及表达式的计算

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、中缀表达式与后缀表达式

1.中缀表达式

中缀表达式就是我们日常见到的表达式写法,即运算符在两个运算数的中间。
例:(5+20+1∗3)/14
中缀表达式中会出现括号,这种表达式如果是人直接来进行计算的话,会比较方便,但如果编写程序,对表达式进行求值时会遇到麻烦。

2.后缀表达式

后缀表达式,又叫逆波兰式,其特点是操作符在操作数的后面。如上述中缀表达式可以转化为后缀表达式:
520+13∗+14/
当中缀表达式转换为后缀表达式后,式中的括号会取消掉(因为此时可以通过运算符的位置来决定运算的优先级)。这种表达方式虽然对人不友好,但对于计算机来说可以直接从左向右遍历表达式进行表达式求值。

二、将中缀表达式转化为后缀表达式

当我们拿到一个中缀表达式的字符串时,首先我们要将此字符串进行分割,在这里我们创建一个unit类储存算式中的操作数和操作符,其中的isNum方法用于判断此对象存储的是操作数还是操作符。如果是操作数,则value存储操作数的值;如果是操作符,则value存储操作符的权值(加减为0,乘除模为1,其他为2),op_ch存储操作符的具体值。
类实现如下:

class unit{                  //此类存储运算数和运算符,通过isNum区分 
	public:
		bool isNum;
		char op_ch='#';
		double value=0;      //如果isNum为真,value储存数字的值,否则储存运算符的权重 
		unit(string s)
		{
			if(s[0]>='0'&&s[0]<='9')
			{
				isNum=true;
				value=To_num(s);
			}
			else
			{
				isNum=false;
				op_ch=s[0];
				value=Level(op_ch);
			}
		}
		double To_num(string s)
		{
			double num;
			stringstream ss(s);
			ss>>num;
			return num;
		}
		
		int Level(char op)
		{
			if(op=='('||op==')') return 0;
			else if(op=='+'||op=='-') return 1;
			else return 2;
		}
};

分割时是一个双指针算法,每次分割出一个unit,就依次存入vector中:
代码实现如下:

vector<unit> Split_str(string str)    //分割表达式,并返回按顺序存储在vector中的中缀表达式 
{
	vector<unit> v;
	int i=0,j=0;
	while(j<str.size())
	{
		if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'||str[i]=='('||str[i]==')'||str[i]=='%')
		{
			unit temp(str.substr(i,1));
			v.push_back(temp);
			i++,j++;
		}
		else
		{
			while((str[j]>='0'&&str[j]<='9')||str[j]=='.') j++;
			string s=str.substr(i,j-i);
			unit temp(s);
			v.push_back(temp);
			i=j;
		}
	}
	return v;
} 

若我们想把中缀表达式转化为后缀表达式,需要用到栈这种数据结构。首先,我们遍历中缀表达式字符串,遇到数字时,直接写入结果vector中,遇到操作符时,若操作符栈为空,则直接入栈,否则判定此时栈顶元素的权重,当操作符的权重大于栈顶元素时,直接将操作符入栈,否则不断pop出栈顶元素存入结果vector,直到操作符的权重大于栈顶元素,这时将操作符入栈。
对于括号的处理可以这样理解:左括号在入栈前的权值为无穷大,在入栈后的权值为无穷小。当遇到右括号时,不断pop出栈顶元素存入结果vector,直到pop出一个左括号与其进行匹配(注意括号永远不会进入结果vector)。
当表达式遍历结束后,将栈中的元素依次出栈,存入结果vector即可。
代码实现如下:

vector<unit> Exchange(vector<unit> v) //传入储存在vector中的中缀表达式,返回后缀表达式 
{
	vector<unit> res;
	stack<unit> op; 
	for(int i=0;i<v.size();i++)
	{
		if(v[i].isNum) res.push_back(v[i]);
		else if(v[i].op_ch=='(') op.push(v[i]);
		else if(v[i].op_ch==')')
		{
			while(op.top().op_ch!='(') 
			{
				res.push_back(op.top());
				op.pop();
			}
			op.pop();
		}
		else
		{
			if(op.empty()) op.push(v[i]);
			else
			{
				while(op.top().value>=v[i].value)
				{
					res.push_back(op.top());
					op.pop(); 
					if(op.empty()) break;
				}
				op.push(v[i]);
			}
		}
	}
	while(!op.empty())
	{
		res.push_back(op.top());
		op.pop();
	}
	return res;
}

三、后缀表达式的计算

当我们成功转化为后缀表达式后,计算结果就想当简单了。首先我们需要一个栈来存储所有的操作数。遍历后缀表达式,操作数直接入栈,每当遇到操作符时,pop出栈顶的两个操作数,我们在这里将其分别称为栈顶和次栈顶元素吧。然后进行计算:

temp=次栈顶元素 操作符 栈顶元素(注意这里的前后顺序)

最后将计算出的结果直接入栈。这样,当后缀表达式遍历结束后,栈中唯一的元素即为表达式的运算结果。
代码实现如下:

double Calculate(vector<unit> v)     //计算并返回后缀表达式的值 
{
	double res;
	stack<double> num;
	for(int i=0;i<v.size();i++)
	{
		if(v[i].isNum) num.push(v[i].value);
		else
		{
			double a=num.top();
			num.pop();
			double b=num.top();
			num.pop();
			if(v[i].op_ch=='+') num.push(b+a);
			if(v[i].op_ch=='-') num.push(b-a);
			if(v[i].op_ch=='*') num.push(b*a);
			if(v[i].op_ch=='/')
			{
				if(a==0)
				{
					cout<<"除0错误!"<<endl;
					exit(1);
				} 
				num.push(b/a);
			}
			if(v[i].op_ch=='%')
			{
				if(a==0)
				{
					cout<<"模0错误!"<<endl;
					exit(1);
				}
				num.push(Mod(b,a));
			}
		}
	}
	return num.top();
}

最后完善一下错误检查机制,完整代码如下:

//本程序可实现表达式运算,并可以检查表达式括号匹配情况,表达式输入是否合法,是否出现除0或模0错误 
#include<iostream>
#include<sstream>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
class unit{                  //此类存储运算数和运算符,通过isNum区分 
	public:
		bool isNum;
		char op_ch='#';
		double value=0;      //如果isNum为真,value储存数字的值,否则储存运算符的权重 
		unit(string s)
		{
			if(s[0]>='0'&&s[0]<='9')
			{
				isNum=true;
				value=To_num(s);
			}
			else
			{
				isNum=false;
				op_ch=s[0];
				value=Level(op_ch);
			}
		}
		double To_num(string s)
		{
			double num;
			stringstream ss(s);
			ss>>num;
			return num;
		}
		
		int Level(char op)
		{
			if(op=='('||op==')') return 0;
			else if(op=='+'||op=='-') return 1;
			else return 2;
		}
};

double Mod(double a,double b)         //取模 
{
	double aa=abs(a);
	double bb=abs(b);
	while(aa>bb) aa-=bb;
	if(a*b>0) return aa;
	else return bb;
}

bool Check(vector<unit> v)           //检查表达式括号是否匹配 
{
	stack<char> s;
	for(int i=0;i<v.size();i++)
	{
		if(v[i].op_ch=='(') s.push(v[i].op_ch);
		if(v[i].op_ch==')')
		{
			if(s.empty()) return false;
			s.pop();
		}
	}
	if(!s.empty()) return false;
	else return true;
}
bool Check_num(string s)            //检查数字是否合法 
{
	if(!(s[0]>='0'&&s[0]<='9')) return false;
	int point=0;
	for(int i=0;i<s.size();i++)
	{
		if(s[i]=='.') point++;
		if(!((s[i]>='0'&&s[i]<='9')||s[i]=='.')) return false;
	}
	if(point>1) return false;
	else return true;
}

vector<unit> Split_str(string str)    //分割表达式,并返回按顺序存储在vector中的中缀表达式 
{
	vector<unit> v;
	int i=0,j=0;
	while(j<str.size())
	{
		if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'||str[i]=='('||str[i]==')'||str[i]=='%')
		{
			unit temp(str.substr(i,1));
			v.push_back(temp);
			i++,j++;
		}
		else
		{
			while((str[j]>='0'&&str[j]<='9')||str[j]=='.') j++;
			string s=str.substr(i,j-i);
			if(!Check_num(s))
			{
				cout<<endl<<"存在非法数字或符号!"<<endl;
				exit(1); 
			}
			unit temp(s);
			v.push_back(temp);
			i=j;
		}
	}
	return v;
} 
vector<unit> Exchange(vector<unit> v) //传入储存在vector中的中缀表达式,返回后缀表达式 
{
	if(!Check(v))
	{
		cout<<endl<<"括号不匹配,表达式错误!"<<endl;
		exit(1); 
	}
	vector<unit> res;
	stack<unit> op; 
	for(int i=0;i<v.size();i++)
	{
		if(v[i].isNum) res.push_back(v[i]);
		else if(v[i].op_ch=='(') op.push(v[i]);
		else if(v[i].op_ch==')')
		{
			while(op.top().op_ch!='(') 
			{
				res.push_back(op.top());
				op.pop();
			}
			op.pop();
		}
		else
		{
			if(op.empty()) op.push(v[i]);
			else
			{
				while(op.top().value>=v[i].value)
				{
					res.push_back(op.top());
					op.pop(); 
					if(op.empty()) break;
				}
				op.push(v[i]);
			}
		}
	}
	while(!op.empty())
	{
		res.push_back(op.top());
		op.pop();
	}
	return res;
}
double Calculate(vector<unit> v)     //计算并返回后缀表达式的值 
{
	double res;
	stack<double> num;
	for(int i=0;i<v.size();i++)
	{
		if(v[i].isNum) num.push(v[i].value);
		else
		{
			double a=num.top();
			num.pop();
			double b=num.top();
			num.pop();
			if(v[i].op_ch=='+') num.push(b+a);
			if(v[i].op_ch=='-') num.push(b-a);
			if(v[i].op_ch=='*') num.push(b*a);
			if(v[i].op_ch=='/')
			{
				if(a==0)
				{
					cout<<"除0错误!"<<endl;
					exit(1);
				} 
				num.push(b/a);
			}
			if(v[i].op_ch=='%')
			{
				if(a==0)
				{
					cout<<"模0错误!"<<endl;
					exit(1);
				}
				num.push(Mod(b,a));
			}
		}
	}
	return num.top();
}

int main()
{ 
	string str;
	cin>>str;
	vector<unit> exp;
	exp=Split_str(str);
	cout<<"分割结果:"; 
	for(int i=0;i<exp.size();i++)
	{
		if(exp[i].isNum) cout<<exp[i].value<<'|';
		else cout<<exp[i].op_ch<<'|';
	}
	exp=Exchange(exp);
	cout<<endl<<"后缀表达式:";
	for(int i=0;i<exp.size();i++)
	{
		if(exp[i].isNum) cout<<exp[i].value<<' ';
		else cout<<exp[i].op_ch<<' ';
	}
	cout<<endl<<"运算结果:";
	cout<<Calculate(exp)<<endl;

	return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
中缀表达式后缀表达式是一种常用的算法问题。中缀表达式是我们常见的数学表达式形式,例如(3 + 4) * 5 - 6,而后缀表达式(也叫逆波兰表达式)则是将操作符放在操作数之后的表达式形式,例如3 4 + 5 * 6 -。 进行中缀表达式后缀表达式的算法可以使用来实现。具体步骤如下: 1. 创建一个空和一个空列表,用于存储操作符和最终的后缀表达式; 2. 从左到右遍历中缀表达式的每个字符; 3. 如果当前字符是数字或字母,则将其添加到后缀表达式的列表中; 4. 如果当前字符是左括号,则将其压入中; 5. 如果当前字符是右括号,则将中的操作符依次弹出并添加到后缀表达式的列表中,直到遇到左括号为止; 6. 如果当前字符是操作符,则判断顶操作符的优先级,如果顶操作符的优先级高于等于当前操作符,则将顶操作符弹出并添加到后缀表达式的列表中,重复这一步骤直到顶操作符的优先级低于当前操作符或为空,最后将当前操作符压入中; 7. 遍历完中缀表达式后,将中的操作符依次弹出并添加到后缀表达式的列表中; 8. 最终得到的列表即为转换后的后缀表达式。 以中缀表达式(3 + 4) * 5 - 6为例,按照上述算法进行转换得到后缀表达式3 4 + 5 * 6 -。 通过这个算法,我们可以将中缀表达式转换为后缀表达式,这种形式更适合计算机进行解析和计算

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值