表达式相关(一)操作数栈、运算符栈

NOIP2013普及组T2

只有加法和乘法的表达式

思考:
使用tok来存放操作数或操作符(在编译器词法分析中称之为token,故简写为tok);输入只有一行可以用fgets,不知道题目给的输入文件有没有换行(fgets是会读入换行符的),所以还要加个判断,不然存放的时候会把换行符也当做运算符

对于2+3+...的表达式,读到2+3之后可以直接先算出5,结果是与5+...等价的;然而读到2+3...的表达式,需要先计算3...后面的内容,因为乘法的优先级比加法要高;故只读到2+3的时候不能先做计算,反之读到2*3可以先算。

每次读到操作数肯定要进栈;读到操作符,则根据优先级进行判断和计算;因此需要两个栈

一个简单的便于理解的小图示


大致推断出过程:

解析token
若为操作数则入栈
若为运算符:
若运算符栈为空,则将当前运算符入栈
若当前运算符优先级高于栈顶运算符,则将当前运算符入栈
否则不断循环,直至运算符栈为空或者当前运算符优先级高于栈顶运算符:
栈顶两个操作数出栈
栈顶运算符出栈
将两个操作数进行计算,再进栈
循环结束,将当前运算符入栈

实际写代码时考虑到判断运算符栈为空比较困难,需要一个哨兵;

大致长这样,关于括号的事情后面再写

#include<bits/stdc++.h>
using namespace std;
char tok[100000][20];
int tok_max=0;
char s[500000];

int calc(char op,int num1,int num2){
	assert(op=='+'||op=='*');
	if(op=='+')return num1+num2;
	if(op=='*')return num1*num2;
}

int priority(char op){
	if(op=='*')return 2;
	if(op=='+')return 1;
}
int main(){
	fgets(s,sizeof(s),stdin);
	int num=0;
	if(s[strlen(s)-1]=='\n'){
		s[strlen(s)-1]=0;
	}
	for(int i=0;i<strlen(s);i++){
		if(isdigit(s[i])){
			num=num*10+s[i]-'0';
		}
		else{
			++tok_max;++tok_max;
			sprintf(tok[tok_max-1],"%d",num);
			*tok[tok_max]=s[i];
			num=0;
		}
	}
	++tok_max;
	sprintf(tok[tok_max],"%d",num);
	
	//printf("tok_max..%d\n",tok_max);
	//for(int i=1;i<=tok_max;i++){
	//	puts(tok[i]);
	//}
	
	stack<int>optr,opnd;
	//运算符栈、操作数栈
	optr.push('#');//检验运算符栈非空的哨兵 
	for(int i=1;i<=tok_max;i++){
		if(isdigit(*tok[i])){
			int num1;
			sscanf(tok[i],"%d",&num1);
			opnd.push(num1);
		}
		else{
			for(;optr.top()!='#' && priority(*tok[i]) <= priority(optr.top());){
				int num2=opnd.top();opnd.pop();
				int num3=opnd.top();opnd.pop();
				char op=optr.top();optr.pop();
				opnd.push(calc(op,num3,num2)%10000);
			}
			optr.push(*tok[i]);
		}
	}
	//printf("%d\n",opnd.top());
	
	for(;optr.top()!='#';){
		int num2=opnd.top();opnd.pop();
		int num3=opnd.top();opnd.pop();
		char op=optr.top();optr.pop();
		opnd.push(calc(op,num3,num2)%10000);
	}
	printf("%d\n",opnd.top()%10000);

	return 0;
}
posted @ 2024-08-07 11:24  计算机知识杂谈  阅读(38)  评论(0编辑  收藏  举报