NYOJ-35-表达式求值
Posted f_zyj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NYOJ-35-表达式求值相关的知识,希望对你有一定的参考价值。
描述
ACM队的mdd想做一个计算器,但是,他要做的不仅仅是一计算一个A+B的计算器,他想实现随便输入一个表达式都能求出它的值的计算器,现在请你帮助他来实现这个计算器吧。
比如输入:“1+2/4=”,程序就输出1.50(结果保留两位小数)
输入
第一行输入一个整数n,共有n组测试数据(n<10)。
每组测试数据只有一行,是一个长度不超过1000的字符串,表示这个运算式,每个运算式都是以“=”结束。这个表达式里只包含+-*/与小括号这几种符号。其中小括号可以嵌套使用。数据保证输入的操作数中不会出现负数。
数据保证除数不会为0
输出
每组都输出该组运算式的运算结果,输出结果保留两位小数。
样例输入
2
1.000+2/4=
((1+2)*5+1)/4=
样例输出
1.50
4.00
题目比较容易理解,就是实现四则运算。
说到四则运算,不得不提到逆波兰表示法,也就是后缀表示法。这种表示法不需要括号,对于9 + (3 - 1) x 3 + 10 ÷ 2用后缀表示法的样子则是:9 3 1 - 3 * + 10 2 / +。之所以叫后缀的原因是,所有的运算符号都是要在运算数字的后边出现,那么很容易的想到,我们平时写的式子,也就是第一个式子,是中缀表示法。
中缀表达式转后缀表达式规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号(乘除优先于加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止,这里我们需要一个栈来实现,用于符号的进栈出栈。
9 + (3 - 1) x 3 + 10 ÷ 2(中缀表达式) >>> 9 3 1 - 3 * + 10 2 / +(后缀表达式)
后缀表达式使用规则:从左到右遍历后缀表达式的每一个数字和符号,遇到是数字就进栈,遇到是符号,就当处于栈顶的两个数字出栈进行运算,运算结果进栈,一直到最终获得结果。
这里存在两个过程,
1.将中缀表达式转化为后缀表达式(栈用来进出运算符号)。
2.将后缀表达式进行运算得到结果(栈用来进出运算的数字)。
代码如下:
/*
不知道为啥一直WA,可是我试了很多数据都可以的,哎,头疼死了。暂且记下,来日再战,我需要静静。
后边的两个代码均是AC代码。
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define INF 10000000
#define ADD INF + 1 //+
#define SUB INF + 2 //-
#define MUL INF + 3 //x
#define DIV INF + 4 //÷
char str[1005]; //原公式
int len; //公式长度
char symbol[1000]; //运算符号栈
float suffix[1005]; //后缀式
float answer[1000]; //运算栈
//从s[*pc]开始获取一个浮点数
int StrToInt(char s[], int * pc, float *pout)
{
char buf[100];
int i = 0;
if(s[*pc]<‘0‘ || s[*pc]>‘9‘)
return 1;
else
{
while((s[*pc] >= ‘0‘ && s[*pc] <= ‘9‘) || s[*pc] == ‘.‘)
{
buf[i] = s[*pc];
(*pc)++;
i++;
}
buf[i] = ‘\0‘;
*pout = (float)atof(buf);
return 0;
}
}
void swi(int *key, char sym)
{
switch (sym)
{
case ‘+‘:
suffix[(*key)++] = ADD;
break;
case ‘-‘:
suffix[(*key)++] = SUB;
break;
case ‘*‘:
suffix[(*key)++] = MUL;
break;
case ‘/‘:
suffix[(*key)++] = DIV;
break;
}
}
void perform(int suf, int *_key)
{
float a = answer[(*_key)--];
float b = answer[(*_key)];
float c;
switch (suf)
{
case (INF + 1):
c = a + b;
break;
case (INF + 2):
c = b - a;
break;
case (INF + 3):
c = a * b;
break;
default:
c = b / a;
break;
}
answer[*_key] = c;
// printf("%.2f\n", answer[*_key]);
return ;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%s", str);
len = (int)strlen(str) - 1;
int key = 0;
int top = -1;
float pout;
//中缀式转后缀式
for (int i = 0; i < len; )
{
if (!StrToInt(str, &i, &pout))
{
suffix[key++] = pout;
}
else if (top != -1 && str[i] == ‘)‘)
{
while (symbol[top] != ‘(‘)
{
swi(&key, symbol[top--]);
}
i++;
top--;
}
else if (top != -1 && (str[i] == ‘+‘ || str[i] == ‘-‘) && (symbol[top] == ‘*‘ || symbol[top] == ‘/‘))
{
while (symbol[top] != ‘(‘ && top >= 0)
{
swi(&key, symbol[top--]);
}
symbol[++top] = str[i++];
}
else if (top != -1 && (str[i] == ‘*‘ || str[i] == ‘/‘) && (symbol[top] == ‘*‘ || symbol[top] == ‘/‘))
{
swi(&key, symbol[top]);
symbol[top] = str[i++];
}
else
{
symbol[++top] = str[i++];
}
}
while (top >= 0)
{
swi(&key, symbol[top--]);
}
// for (int i = 0; i < key; i++)
// {
// printf("%f ", suffix[i]);
// }
// printf("\n");
//后缀式运算
int _key = -1;
for (int i = 0; i < key; i++)
{
if (suffix[i] < INF)
{
answer[++_key] = suffix[i];
// printf("%.2f\n", answer[_key]);
}
else
{
perform((int)suffix[i], &_key);
}
}
printf("%.2f\n", answer[0]);
}
return 0;
}
另外还有两种方法,第一种是将字符和数据分别入两个栈,然后根据优先级的比较,对数据栈顶的两个元素进行出栈操作然后进栈。
#include<stdio.h>
#include<stdlib.h>
//数据栈
typedef struct DA
{
float data[1000];
int pop;
} SDA;
//运算符栈
typedef struct OP
{
char op[1000];
int pop;
} SOP;
//初始化数据栈
int InitSDA(SDA * p)
{
p->pop = 0;
return 0;
}
//初始化运算符栈
int InitSOP(SOP * p)
{
p->pop = 0;
(p->op[p->pop]) = ‘=‘;
(p->pop)++;
return 0;
}
//数据入栈
int PushSDA(SDA * p, float d)
{
if(p->pop < 1000)
{
p->data[p->pop] = d;
(p->pop)++;
return 0;
}
else
return 1; //栈满
}
//运算符入栈
int PushSOP(SOP * p, char c)
{
if(p->pop < 1000)
{
p->op[p->pop] = c;
(p->pop)++;
return 0;
}
else
return 1; //栈满
}
//数据出栈
int PopSDA(SDA * p, float * d)
{
(p->pop)--;
if(p->pop >= 0)
{
*d = p->data[p->pop];
return 0;
}
else
return 1;
}
//运算符出栈
int PopSOP(SOP * p, char * c)
{
(p->pop)--;
if(p->pop >= 0)
{
*c = p->op[p->pop];
return 0;
}
else
return 1;
}
//从s[*pc]开始获取一个浮点数
int StrToInt(char s[], int * pc, float *pout)
{
char buf[100];
int i = 0;
if(s[*pc]<‘0‘ || s[*pc]>‘9‘)
return 1;
else
{
while((s[*pc] >= ‘0‘ && s[*pc] <= ‘9‘) || s[*pc] == ‘.‘)
{
buf[i] = s[*pc];
(*pc)++;
i++;
}
buf[i] = ‘\0‘;
*pout = (float)atof(buf);
return 0;
}
}
//从s[*pc]获取一个char
int StrToChar(char s[], int *pc, char *pout)
{
if(‘+‘==s[*pc] || ‘-‘==s[*pc] || ‘*‘==s[*pc] || ‘/‘==s[*pc] || ‘(‘==s[*pc] || ‘)‘==s[*pc])
{
*pout = s[*pc];
(*pc)++;
return 0;
}
else
return 1;
}
//获取优先级
char GetPri(char c1, char c2)
{
char f[7][7] = {‘>‘, ‘>‘, ‘<‘, ‘<‘, ‘<‘, ‘>‘, ‘>‘,
‘>‘, ‘>‘, ‘<‘, ‘<‘, ‘<‘, ‘>‘, ‘>‘,
‘>‘, ‘>‘, ‘>‘, ‘>‘, ‘<‘, ‘>‘, ‘>‘,
‘>‘, ‘>‘, ‘>‘, ‘>‘, ‘<‘, ‘>‘, ‘>‘,
‘<‘, ‘<‘, ‘<‘, ‘<‘, ‘<‘, ‘=‘, ‘\0‘,
‘>‘, ‘>‘, ‘>‘, ‘>‘, ‘\0‘, ‘>‘, ‘>‘,
‘<‘, ‘<‘, ‘<‘, ‘<‘, ‘<‘, ‘\0‘, ‘=‘,};
int i=0, j=0;
switch(c1)
{
case ‘+‘: i = 0; break;
case ‘-‘: i = 1; break;
case ‘*‘: i = 2; break;
case ‘/‘: i = 3; break;
case ‘(‘: i = 4; break;
case ‘)‘: i = 5; break;
case ‘=‘: i = 6; break;
}
switch(c2)
{
case ‘+‘: j = 0; break;
case ‘-‘: j = 1; break;
case ‘*‘: j = 2; break;
case ‘/‘: j = 3; break;
case ‘(‘: j = 4; break;
case ‘)‘: j = 5; break;
case ‘=‘: j = 6; break;
}
return f[i][j];
}
//计算表达式
float Operate(float a, char op, float b)
{
switch(op)
{
case ‘+‘: return a + b;
case ‘-‘: return a - b;
case ‘*‘: return a * b;
case ‘/‘: return a / b;
default: return 0;
}
}
int main(void)
{
char s[10][1000];
int c = 0;
float bufda;
char bufop;
float a, b;
SDA sda;
SOP sop;
int n;
int i;
scanf("%d", &n);
for(i = 0; i < n; i++)
scanf("%s", s[i]);
for(i = 0; i < n; i++)
{
c = 0;
InitSDA(&sda); //初始化数据栈
InitSOP(&sop); //初始化符号栈
while(s[i][c] != ‘=‘ || sop.op[sop.pop - 1] != ‘=‘) // 计算未完成
{
if(0 == StrToInt(s[i], &c, &bufda))
PushSDA(&sda, bufda); //数据入栈
else
{
switch(GetPri(sop.op[sop.pop - 1], s[i][c]))
{
case ‘<‘:
if(0 == StrToChar(s[i], &c, &bufop))
PushSOP(&sop, bufop);
break;
case ‘=‘:
PopSOP(&sop, &bufop);
c++;
break;
case ‘>‘:
PopSOP(&sop, &bufop);
PopSDA(&sda, &b);
PopSDA(&sda, &a);
PushSDA(&sda, Operate(a, bufop, b));
break;
}
}
}
PopSDA(&sda, &a);
printf("%.2f\n", a);
}
return 0;
}
//改写成C++,并简化后如此
//#include <stack>
//#include <stdio.h>
//#include <ctype.h>
//#include <string.h>
//#include <stdlib.h>
//
//using namespace std;
//
//int priority(char c)
//{
// if(c == ‘=‘) return 0;
// if(c == ‘+‘) return 1;
// if(c == ‘-‘) return 1;
// if(c == ‘*‘) return 2;
// if(c == ‘/‘) return 2;
// return 0;
//}
//
//void compute(stack<double>& Num,stack<char>& Op)
//{
// double b = Num.top();
// Num.pop();
// double a = Num.top();
// Num.pop();
// switch(Op.top())
// {
// case ‘+‘:Num.push(a+b);break;
// case ‘-‘:Num.push(a-b);break;
// case ‘*‘:Num.push(a*b);break;
// case ‘/‘:Num.push(a/b);break;
// }
// Op.pop();
//}
//
//int main()
//{
// int z;
// char str[1005];
// stack<double> Num;
// stack<char> Op;
// scanf("%d",&z);
// while(z--)
// {
// scanf("%s",str);
// int len = strlen(str);
// for(int i=0;i<len;i++)
// {
// if(isdigit(str[i]))
// {
// double n = atof(&str[i]);
// while(i<len && (isdigit(str[i]) || str[i]==‘.‘))
// i++;
// i--;
// Num.push(n);
// }
// else
// {
// if(str[i] == ‘(‘)
// Op.push(str[i]);
// else if(str[i] == ‘)‘)
// {
// while(Op.top()!=‘(‘)
// compute(Num,Op);
// Op.pop();
// }
// else if(Op.empty() || priority(str[i])>priority(Op.top()))
// Op.push(str[i]);
// else
// {
// while(!Op.empty() && priority(str[i])<=priority(Op.top()))
// compute(Num,Op);
// Op.push(str[i]);
// }
// }
// }
// Op.pop();
// printf("%.2f\n",Num.top());
// Num.pop();
// }
// return 0;
//}
第二种是动态规划,将一个大问题切割成些许小问题,和归并排序的思想相仿。
// AC(动态规划)
#include<stdio.h>
#include<string.h>
int len;
int fst[1005];
char str[1005];
double Jud(int begin, int end); /*计算并返回表达式在区间[begin end]中的值*/
int main()
{
int T, i;
double ans;
scanf("%d", &T);
while(T--)
{
memset(fst, 0, sizeof(fst)); /*一定要清0*/
scanf("%s", str);
len = (int)strlen(str)-1;
fst[0] = 1;
for(i = 1; i <= len - 1; i++) /*fst[i]表示优先级,fst[i]越大,说明优先级越高↓↓*/
{ /*例如str[] -- ((1+2)*5+1)/4=*/
if(str[i - 1]== ‘(‘) /*对应fst[] -- 12333222221110*/
fst[i] = fst[i - 1] + 1;
else if(str[i] == ‘)‘)
fst[i] = fst[i - 1] - 1;
else
fst[i] = fst[i - 1];
}
ans = Jud(0, len - 1); /*传入整个表达式,不包括=*/
printf("%.2f\n", ans);
}
return 0;
}
double Jud(int begin, int end)
{ /*规定区间[begin, end]的优先级标准为fst[begin]*/
int i;
double k;
for(i = begin; i <= end; i++) /*先从做左到右找到第一个处于指定优先级的‘+‘运算符*/
{
if(str[i]== ‘+‘ && fst[i] == fst[begin])
{
k = Jud(begin, i - 1) + Jud(i + 1, end); /*将其拆成两个个表达式的和*/
return k;
}
}
for(i = end; i >= begin; i--) /*如果找不到‘+‘,再从右往左找到第一个处于指定优先级的‘-‘运算符*/
{
if(str[i]==‘-‘ && fst[i] == fst[begin])
{
k = Jud(begin, i - 1) - Jud(i + 1, end); /*将其拆成两个个表达式的差*/
return k;
}
}
for(i = begin; i <= end; i++) /*如果还找不到,再从左往右找到第一个处于指定优先级的‘*‘运算符*/
{
if(str[i] == ‘*‘ && fst[i] == fst[begin])
{
k = Jud(begin, i - 1) * Jud(i + 1, end); /*将其拆成两个个表达式的积*/
return k;
}
}
for(i = end; i >= begin; i--) /*同上,从右往左找到第一个处于指定优先级的‘/‘运算符*/
{
if(str[i] == ‘/‘ && fst[i] == fst[begin])
{
k = Jud(begin, i - 1) / Jud(i + 1, end); /*将其拆成两个个表达式的商*/
return k;
}
}
if(str[begin]==‘(‘) /*如果在这个[begin,end]区间里的指定优先级中没有任何运算符,说明此区间可能完全包含上一级*/
{
for(i = begin + 1; fst[i] >= fst[begin + 1]; i++);
k = Jud(begin + 1, i - 1);
}
else /*既然没有包含上一级,说明这个区间就只剩下一个数啦*/
{
char *p = str;
sscanf(p+begin, "%lf", &k); /*将这个数赋值给k,并返回*/
}
return k;
}
理论上这三种均可以AC…可是我不知道第一种哪里出了BUG,等回头有空了再看吧…就这样吧。
以上是关于NYOJ-35-表达式求值的主要内容,如果未能解决你的问题,请参考以下文章