郁闷的C小加(三)
时间限制:
1000 ms | 内存限制:
65535 KB
难度:
4
-
描述
-
聪明的你帮助C小加解决了中缀表达式到后缀表达式的转换(详情请参考“郁闷的C小加(一)”),C小加很高兴。但C小加是个爱思考的人,他又想通过这种方法计算一个表达式的值。即先把表达式转换为前缀和后缀表达式,再求值。这时又要考虑操作数是小数和多位数的情况。
-
输入
-
第一行输入一个整数T,共有T组测试数据(T<10)。
每组测试数据只有一行,是一个长度不超过1000的字符串,表示这个运算式,每个运算式都是以“=”结束。这个表达式里只包含+-*/与小括号这几种符号。其中小括号可以嵌套使用。数据保证输入的操作数中不会出现负数并且小于1000000。
数据保证除数不会为0。
输出
- 对于每组测试数据输出结果包括三行,先输出转换后的前缀和后缀表达式,再输出计算结果,结果保留两位小数。 样例输入
-
2 1+2= (19+21)*3-4/5=
样例输出
-
+ 1 2 = 1 2 + = 3.00 - * + 19 21 3 / 4 5 = 19 21 + 3 * 4 5 / - = 119.20
-
第一行输入一个整数T,共有T组测试数据(T<10)。
思路: 找到规律、确定优先级即可,个人感觉中缀转前缀较难想,倒叙遍历中缀表达式寻找规律。。
代码:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stack>
#define N 1050
using namespace std;
char s[N];
stack<double>Num; // 数据栈
stack<char>Ope; // 符号栈
double JieXi();
double JiSuan();
void To_Prefix(){ // 中缀式转前缀式
char n[N * 3] = {"\0"}; // n 数组保存前缀式
int len = strlen(s), ni = 0; // ni 为 n 数组的下标
for(int i = len - 2; i >= 0; i --){ // 倒叙遍历s数组
if(isdigit(s[i]) || s[i] == '.'){ // 数字直接保存到前缀式数组
n[ni ++] = s[i];
}
else{
if(ni != 0 && n[ni - 1] != ' '){ // 遇到非数字加空格,但要排除最后一个字符是括号(即ni = 0 还没有数字进入)的情况
n[ni ++] = ' ';
}
switch(s[i]){
case '+': // +、- 优先级低于 *、/
case '-':
while(!Ope.empty() && (Ope.top() == '*' || Ope.top() == '/')){
n[ni ++] = Ope.top();
n[ni ++] = ' ';
Ope.pop();
}
break;
case '*': // * 、 ) 直接进栈
case '/':
case ')':
break;
case '(': // 右括号时,将栈中)前的放入前缀式数组
while(Ope.top() != ')'){
n[ni ++] = Ope.top();
n[ni ++] = ' ';
Ope.pop();
}
Ope.pop();
break;
}
if(s[i] != '('){ // 左括号不进栈
Ope.push(s[i]);
}
}
}
while(n[ni - 1] == ' '){ // 先消去前缀式数组尾部的空格,与下方统一
ni --;
}
while(!Ope.empty()){ // 将栈中元素弹出,存入前缀式数组
n[ni ++] = ' ';
n[ni ++] = Ope.top();
Ope.pop();
}
for(int i = ni - 1; i >= 0; i --){ // 输出前缀式
printf("%c", n[i]);
}
printf(" =\n");
}
void To_Postfix(){ // 中缀式转后缀式
for(int i = 0; ; i ++){
if(isdigit(s[i]) || s[i] == '.'){ // 数字直接输出
printf("%c", s[i]);
}
else{
if(i > 0 && isdigit(s[i - 1])){ // 数字后输出空格
printf(" ");
}
switch(s[i]){
case '(': // ( 直接入栈
break;
case ')': // 出栈输出、直至遇到 (
while(Ope.top() != '('){
printf("%c ", Ope.top());
Ope.pop();
}
Ope.pop(); // 弹出(
break;
case '+': // +、-、= 优先级最低,只要栈不空栈顶不是(, 则出栈输出
case '-':
case '=':
while(!Ope.empty() && Ope.top() != '('){
printf("%c ", Ope.top());
Ope.pop();
}
break;
case '*': // *、/ 优先级高,只有栈顶是 * / 时才输出
case '/':
while(!Ope.empty() && (Ope.top() == '*' || Ope.top() == '/')){
printf("%c ", Ope.top());
Ope.pop();
}
break;
}
if(s[i] == '='){
printf("=\n");
break;
}
else{
if(s[i] != ')'){ // ) 不入栈
Ope.push(s[i]);
}
}
}
}
}
int main(){
int loop;
scanf("%d", &loop);
while(loop --){
scanf(" %s", s);
To_Prefix(); // 中缀转前缀
To_Postfix(); // 中缀转后缀
double ans = JieXi(); // 表达式求值
printf("%.2lf\n", ans);
}
return 0;
}
/ 下为之前表达式求值代码
double JieXi(){ // 表达式求值 —解析函数
int i = -1, ni = 0; // i为输入表达式(字符串)下标 ,ni为num数组下标;
char num[100] = {"\0"}; // 暂存数字字符和小数点,用于将字符串转换为数字
double ans = 0; // 记录每一次计算的结果
while(1){
i ++;
if(isdigit(s[i]) || s[i] == '.'){ // 如果当前字符是数字或小数点,将其直接放入数字暂存数组
num[ni ++] = s[i];
}
else{ // 当前字符为运算符
if(ni != 0){ // 如果数字暂存数组中有数据,转换成数字,并压入数据栈
sscanf(num, "%lf", &ans);
Num.push(ans);
memset(num, '\0', sizeof(num)); // 入栈后清空暂存数组,其下标置零
ni = 0;
}
switch(s[i]){ // 分析运算符,并开始计算
case '+':
case '-':
case '=':
while(!Ope.empty() && Ope.top() != '('){ // 其它运算符优先级都高于+、-、=,先全部运算直至符号栈栈顶为'('或栈空
ans = JiSuan();
Num.push(ans); // 记得将运算结果压入数据栈,当前运算符break后压入
}
break;
case '*':
case '/':
while(!Ope.empty() && (Ope.top() == '*' || Ope.top() == '/')){
ans = JiSuan();
Num.push(ans);
}
break;
case ')':
while(Ope.top() != '('){ // 运算直至符号栈栈顶为(
ans = JiSuan();
Num.push(ans);
}
Ope.pop(); // 将栈顶的(弹出
break;
case '(': // ( 之后直接如符号栈即可,无需运算
break;
}
if(s[i] == '='){
Num.pop(); // 此次弹出后,数据栈已清空;并且在之前运算中符号栈已清空(有运算符就有运算)
break; // 运算结束
}
else{
if(s[i] != ')'){ // )、=不入栈,其余运算符都入栈
Ope.push(s[i]);
}
}
}
}
return ans;
}
double JiSuan(){ // 计算函数,自身从栈中取数据
double b = Num.top();
Num.pop();
double a = Num.top();
Num.pop();
char c = Ope.top();
Ope.pop();
switch(c){
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
}
}