【洛古 P1175】表达式的转换

文章描述了一个程序设计问题,涉及到将中缀表达式转换为后缀表达式(逆波兰表示法)并进行计算的过程。通过使用栈数据结构,根据运算符的优先级规则,逐步将中缀表达式转换并输出每个步骤的后缀表达式,最终完成计算并输出结果。示例和解题思路详细解释了如何处理乘方运算以及括号的处理方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

表达式的转换

题目描述

平常我们书写的表达式称为中缀表达式,因为它将运算符放在两个操作数中间,许多情况下为了确定运算顺序,括号是不可少的,而后缀表达式就不必用括号了。

后缀标记法:书写表达式时采用运算紧跟在两个操作数之后,从而实现了无括号处理和优先级处理,使计算机的处理规则简化为:从左到右顺序完成计算,并用结果取而代之。

例如:8-(3+2*6)/5+4 可以写为:8 3 2 6 * + 5 / - 4 +

其计算步骤为:

8 3 2 6 * + 5 / - 4 +
8 3 12 + 5 / - 4 +
8 15 5 / - 4 +
8 3 - 4 +
5 4 +
9

编写一个程序,完成这个转换,要求输出的每一个数据间都留一个空格。

输入格式

就一行,是一个中缀表达式。输入的符号中只有这些基本符号 0123456789+-*/^(),并且不会出现形如 2*-3 的格式。

表达式中的基本数字也都是一位的,不会出现形如 12 形式的数字。

所输入的字符串不要判错。

输出格式

若干个后缀表达式,第 i+1i + 1i+1 行比第 iii 行少一个运算符和一个操作数,最后一行只有一个数字,表示运算结果。

样例 #1

样例输入 #1

8-(3+2*6)/5+4

样例输出 #1

8 3 2 6 * + 5 / - 4 + 
8 3 12 + 5 / - 4 + 
8 15 5 / - 4 + 
8 3 - 4 + 
5 4 + 
9

样例 #2

样例输入 #2

2^2^3

样例输出 #2

2 2 3 ^ ^
2 8 ^
256

提示

运算的结果可能为负数,/ 以整除运算。并且中间每一步都不会超过 2312^{31}231。字符串长度不超过 100100100

注意乘方运算 ^ 是从右向左结合的,即 2 ^ 2 ^ 32 ^ (2 ^ 3),后缀表达式为 2 2 3 ^ ^

其他同优先级的运算是从左向右结合的,即 4 / 2 / 2 * 2((4 / 2) / 2) * 2,后缀表达式为 4 2 / 2 / 2 *

保证不会出现计算乘方时幂次为负数的情况,故保证一切中间结果为整数。


解题思路

STEP 1:写出给定字符串的后缀表达式。

首先,我们要知道如何手算得出后缀表达式。下面是题目样例的后缀表达式转换方法。
在这里插入图片描述
那么如何用编程来实现呢?
我们定义一个函数来判断运算符的优先级。如下:

int check(char c)
{
	switch(c)
	{
		case '+':return 1;
		case '-':return 1;
		case '*':return 2;
		case '/':return 2;
		case '^':return 3;
		case '(':return 0;
		case ')':return 0;
		default:return -1;//程序不会执行这句,保险起见要加上
	}
}

定义两个栈 dat 和 op,它们存的分别是后缀表达式和符号。

来看例子:2 + 3 * 4
在这里插入图片描述
这时,逆序输出栈 dat,得到式子的后缀表达式 2 3 4 * +。

再看一个:2 * 4 + 3
在这里插入图片描述
这时,逆序输出栈 datdatdat,得到式子的后缀表达式 2 4 * 3 +。

经过尝试和拼凑,我们发现:sis_isi 为数字时,直接压进 datdatdat栈。sis_isi为运算符时,优先级若比 op 栈栈顶符号高(是 >>> 而不是 ≥\ge,可以自己模拟看看),就压进栈,否则就弹出 opopop 的栈顶元素到 datdatdat 栈里,直到比栈顶符号优先级高或栈空。

最后,将 opopop 栈里的剩余元素弹出到 datdatdat 栈。

特殊情况
(1)括号
sis_isi为左括号时,可以直接压进 op 栈里。当 sis_isi为右括号时,一直弹出 opopop 栈栈顶到 datdatdat 里,直到栈顶为左括号,再弹出左括号。这些也可以通过模拟得出答案。
看例子:2 + (3 + 4) * 3
在这里插入图片描述
最后逆序输出 dat 栈,得到:2 3 4 + 3 * +

(2)乘方
特殊的乘方运算。模拟样例 2:2 ^ 2 ^ 3
在这里插入图片描述
我们得到:2 2 3 ^ ^
所以,当 sis_isi为乘方运算符时且 opopop 栈栈顶也是乘方运算符时,也可直接压进栈中。

表达式输出:
如果你用的是数组版栈,直接遍历即可。但如果你跟我一样用的是 STL 版栈,可以把栈 op 用来临时存放数据。先把 dat 里的元素全部压进 op 栈,输出字符,再倒回来。

STEP 2:写出计算过程。
为了方便,定义函数用来计算两个数的计算结果。

int js(char c){
	int p1=dat2.top();
	dat2.pop();
	int p2=dat2.top();
	dat2.pop();
	switch(c){
		case '+':return p2+p1;
		case '-':return p2-p1;
		case '*':return p2*p1;
		case '/':return p2/p1;
		case '^':return pow(p2,p1);
		default: return -0x3f3f3f3f;	
	}
}

定义两个栈 numnumnumdat2,dat2dat2,dat2dat2dat2 存储计算过程,numnumnum 用来临时存放数据,之前的 dat 和 op 可以继续使用。

datdatdat 全部弹出到 opopop 里,接下来进行计算。

  • 变量 ttt 获取 opopop 栈顶,opopop 弹出。

  • 若 t 为数字,减去′0′'0'0 再进入 dat2dat2dat2。若 t 为运算符,弹出 dat2dat2dat2 栈顶 2 个元素并记录,将运算结果压进栈,同时输出过程。

  • 重复 1 和 2 两个步骤,直到 op 为空。

输出过程时,反序输出 dat2dat2dat2,正序输出 opopop


代码

#include<bits/stdc++.h>
#define ll long long

using namespace std;
char a[110],ans1[110];
int l,k1,k2;


stack<int>num;
stack<char>dat;
stack<char>op;
stack<int>dat2;

int check(char c){
	switch(c){
		case ')':return 0;
		case '(':return 0;
		case '+':return 1;
		case '-':return 1;
		case '*':return 2;
		case '/':return 2;
		case '^':return 3;
		default: return -1;	
	}
}

int js(char c){
	int p1=dat2.top();
	dat2.pop();
	int p2=dat2.top();
	dat2.pop();
	switch(c){
		case '+':return p2+p1;
		case '-':return p2-p1;
		case '*':return p2*p1;
		case '/':return p2/p1;
		case '^':return pow(p2,p1);
		default: return -0x3f3f3f3f;	
	}
}

void work1(){
	for(int i=1;i<=l;i++)
	{
		if(a[i]>='0'&&a[i]<='9')
			dat.push(a[i]);
		else 
		{
			if(op.empty())
			{
				op.push(a[i]);
				continue;
			}
			char c=op.top();
			if(a[i]=='(')
			{	op.push(a[i]);
				continue;
			}
			if(a[i]==')')
			{
				while(c!='('&&!op.empty())
				{
					dat.push(c);
					op.pop();	
					if(!op.empty())	
						c=op.top();
				}
				op.pop();
				continue;
			}
			if(check(a[i])>check(c))
			{
				op.push(a[i]);
			}
			else{
				if(check(a[i])==check(c)&&check(a[i])==3){
					op.push(a[i]);
					continue;
				}
				while(check(a[i])<=check(c)&&!op.empty())
				{
					dat.push(c);
					op.pop();	
					if(!op.empty())	
						c=op.top();
				}
				op.push(a[i]);
			}
		}
	}

	while(!op.empty())
	{
		dat.push(op.top());	
		op.pop();
	}
	while(!dat.empty())
	{
		op.push(dat.top());	
		dat.pop();
	}	
	while(!op.empty())
	{
		char c=op.top();
		cout<<c<<' ';
		op.pop();
		dat.push(c);
	}
	cout<<endl;
}

void work2(){
	while(!dat.empty())
	{
		char t=dat.top();
		dat.pop();
		op.push(t);
	}
	while(!op.empty())
	{
		char c=op.top();
		if(c>='0'&&c<='9')
		{
			dat2.push(c-'0');
			op.pop();
		}
		else {
			dat2.push(js(c));	
			op.pop();
			while(!dat2.empty())
			{
				num.push(dat2.top());	
				dat2.pop();
			}
			while(!num.empty())
			{
				cout<<num.top()<<' ';
				dat2.push(num.top());
				num.pop();	
			}
			while(!op.empty())
			{
				cout<<op.top()<<' ';	
				dat.push(op.top());	
				op.pop();
			}
			while(!dat.empty())
			{
				op.push(dat.top());	
				dat.pop();
			}
			cout<<endl;
		}
	}
}

int main(){
	scanf("%s",a+1);
	l=strlen(a+1);
	work1();
	work2();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值