C++ 中缀表达式判断合法性并求值

中缀表达式值


题目描述

输入一个中缀表达式(由 0−9 组成的运算数、加 + 减 − 乘 ∗ 除 / 四种运算符、左右小括号组成。注意“−”也可作为负数的标志,表达式以“@”作为结束符)。

判断表达式是否合法,如果不合法,请输出“NO”;
否则请把表达式转换成后缀形式,再求出后缀表达式的值并输出。
注意:必须用栈操作,不能直接输出表达式的值。

输入描述

一行为一个以 @ 结束的字符串。

输出描述

如果表达式不合法,请输出“NO”,要求大写。

如果表达式合法,请输出计算结果。

输入样例:
1+2*8-9@
输出样例
8

简化:[NOIP2013 普及组] 表达式求值


解析

O \mathbf{O} O 输入字符串

I \mathbf{I} I 检查与预处理

  (1)检查 @ 并删除其及其后所有内容

  (2)检查括号匹配
    a. 对于每一位置,其左侧 ( 数量必须大于等于 ) 数量
    b. ( 总数必须等于 ) 总数

  (3)预处理负号(负号前加 0
    a. 字符串开头 -... 改为 0-...
    b. 字符串中间 ...(-...)... 改为 ...(0-...)...

  (4)检查运算符合法性
    a. 字符串内不能有非法符号(即非 1234567890 +-*/ () 的符号)
    b. 运算符(+-*/)两侧必须为数字或括号

I I \mathbf{II} II 中缀表达式转为后缀表达式

  (1)遇到数字直接输出(指加入后缀表达式,下同)

  (2)遇到运算符
    a. 如果栈为空,直接入栈
    b. ( 直接入栈
    c. 遇到 ),弹栈并输出直至遇到 (
    d. 优先级高于栈顶的运算符:直接入栈
    e. 优先级小于等于栈顶的运算符:弹栈并输出至其优先级高于栈顶,而后入栈

I I I \mathbf{III} III 计算后缀表达式的值

  (1)读取到符号:取出栈顶的两个元素运算后入栈

  (2)读取到数字:直接入栈

I V \mathbf{IV} IV 输出栈顶元素

其他细节见代码及注释


代码

#include<cstdio>
#include<string>
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;

const int N=1e5+5;
string st;
pair<bool,int> pf[N]; //<type(0-num,1-op),number/op>
int pftop=0;
stack<char> op; //<operator>

inline int type(char ch)
{
	if(ch>='0'&&ch<='9') return 0;
	if(ch=='+'||ch=='-'||ch=='*'||ch=='/') return 1;
	if(ch=='('||ch==')') return 2;
	return -1;
}
inline int priority(char op)
{
	if(op=='+'||op=='-') return 0;
	if(op=='*'||op=='/') return 1;
	return -1;
}
bool Check_and_Preprocessing()	//1、检查是否合法,并预处理 
{
	bool FlagOfAt=false;
	for(int i=0;i<st.length();i++) //(1)检查是否有'@'并删除其及以后所有内容 
		if(st[i]=='@')
		{
			st.erase(i);
			FlagOfAt=true;
			break;
		}
	if(!FlagOfAt) return false;
	
	int LeftBracketsCount=0;
	for(int i=0;i<st.length();i++) //(2)检查括号是否匹配
	{
		if(st[i]=='(') LeftBracketsCount++;
		if(st[i]==')') LeftBracketsCount--;
		if(LeftBracketsCount<0) return false;
	}
	if(LeftBracketsCount) return false; 
	
	string tmpst;
	for(int i=0;i<st.length();i++) //(3)预处理负号
	{
		if(st[i]=='-')
		{
			if(!i) tmpst+='0'; //a. -...
			else if(st[i-1]=='(') tmpst+='0'; //b. ...(-x)...
		}
		tmpst+=st[i];
	}
	st=tmpst;
	
	if(type(st[0])==1||st[st.length()-1]==1 || type(st[0])==-1) return false; //(4)检查运算符是否合法 
	for(int i=1;i<st.length();i++)
	{
		if(type(st[i])==-1) return false; //a.出现非法符号 
		if(type(st[i])==1) //b.运算符两侧不为数或括号 
		{
			if(type(st[i-1])==1||type(st[i+1])==1) return false; //b i.+*
			if(st[i-1]=='('||st[i+1]==')') return false; //b ii.(+)
		}
	}
	
	return true; //(5)你过关! 
}

void PostfixExpression() //2、先将中缀表达式转换为后缀表达式
{
	for(int i=0;i<st.length();i++) //(1)依次扫描中缀表达式
	{
		if(!type(st[i])) //(2)遇到数字输出到后缀表达式
		{
			int x=0;
			while(st[i]>='0'&&st[i]<='9')
			{
				x=(x<<3)+(x<<1)+(st[i]^48);
				i++;
			}
			pf[++pftop]=make_pair(0,x);
			i--;
		}
		else //(3)遇到运算符,有以下几种情况
		{
			if(op.empty()||st[i]=='(') op.push(st[i]); //a:栈空直接入栈;b:遇到’(‘直接入栈
			else if(st[i]==')') //c:遇到’)‘一直出栈并输出,直到’(‘出栈为止,但’('不作为输出(因为后缀表达式中没有括号)
			{
				while(op.top()!='(')
				{
					pf[++pftop]=make_pair(1,op.top());
					op.pop();
				}
				op.pop();
			}
			else
			{
				if(priority(st[i])>priority(op.top())) //d:遇到运算符优先级高于栈顶运算符优先级,则入栈
					op.push(st[i]);
				else //e:遇到运算符优先级低于或等于栈顶运算符优先级,则出栈,直到其优先级高于栈顶运算符优先级时停止或栈为空),最后再将其入栈
				{
					while(!op.empty() && priority(st[i])<=priority(op.top()))
					{
						pf[++pftop]=make_pair(1,op.top());
						op.pop();
					}
					op.push(st[i]);
				}
			}
		}
	}
	while(!op.empty())
	{
		pf[++pftop]=make_pair(1,op.top());
		op.pop();
	}
	return;
}

stack<int> calc;
inline int calc_op(char op,int x,int y)
{
	if(op=='+') return x+y;
	if(op=='-') return x-y;
	if(op=='*') return x*y;
	if(op=='/') return x/y;
	return 0;
}
inline int Calculate() //3、计算后缀表达式的值 
{
	for(int i=1;i<=pftop;i++)
	{
		if(pf[i].first) //(1)读取到符号 
		{
			int t1=calc.top();calc.pop();
			int t2=calc.top();calc.pop();
			calc.push(calc_op(pf[i].second,t2,t1));
		}
		else calc.push(pf[i].second); //(2)读取到数字 
	}
	return calc.top();
}
int main()
{
	getline(cin,st);
	if(!Check_and_Preprocessing())
	{
		printf("NO\n");
		return 0;
	}
	PostfixExpression();
	int ans=Calculate();
	printf("%d\n",ans);
	return 0;
}
  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
中缀转后缀表达式的步骤如下: 1. 创建一个空的栈和一个空的输出队列。 2. 从左到右遍历中缀表达式的每个元素。 3. 如果遇到操作数(数字),则直接将其添加到输出队列中。 4. 如果遇到左括号,则将其压入栈中。 5. 如果遇到右括号,则将栈中的元素弹出,直到遇到左括号为止,并将弹出的操作符添加到输出队列中。注意,左括号不会添加到输出队列中。 6. 如果遇到操作符,则检查栈顶的操作符。如果栈顶的操作符优先级大于或等于当前操作符的优先级,则将栈顶的操作符弹出,并将其添加到输出队列中。重复此步骤直到栈顶的操作符优先级小于当前操作符的优先级,或者栈为空。然后将当前操作符压入栈中。 7. 遍历完中缀表达式后,如果栈不为空,则将栈中的操作符依次弹出,并添加到输出队列中。 8. 输出队列中的元素就是后缀表达式。 接下来,对后缀表达式进行求值的步骤如下: 1. 创建一个空的栈。 2. 从左到右遍历后缀表达式的每个元素。 3. 如果遇到操作数(数字),则将其压入栈中。 4. 如果遇到操作符,则从栈中弹出两个操作数,并根据操作符进行计算,然后将计算结果压入栈中。 5. 遍历完后缀表达式后,栈中的最后一个元素就是表达式的求值结果。 需要注意的是,对于复杂的表达式,必须按照运算符的优先级和结合性正确地进行计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值