计算器(表达式计算-后缀表达式实现)
【问题描述】
从标准输入中读入一个整数算术运算表达式,如24 / ( 1 + 2 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 )= ,计算表达式结果,并输出。
要求:
1、表达式运算符只有+、-、*、/,表达式末尾的=字符表示表达式输入结束,表达式中可能会出现空格;
2、表达式中会出现圆括号,括号可能嵌套,不会出现错误的表达式;
3、出现除号/时,以整数相除进行运算,结果仍为整数,例如:5/3结果应为1。
4、要求采用逆波兰表达式来实现表达式计算。
【输入形式】
从键盘输入一个以=结尾的整数算术运算表达式。操作符和操作数之间可以有空格分隔。
【输出形式】
在屏幕上输出计算结果(为整数,即在计算过程中除法为整除)。
【样例输入】
24 / ( 1 + 2 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 ) =
【样例输出】
18
【样例说明】
按照运算符及括号优先级依次计算表达式的值。
【评分标准】
通过所有测试点得满分。
分析
- 本题考查使用逆波兰式实现简单计算器的功能。(逆波兰表达式)
- 由于输入是中缀表达式,因此要先将中缀表达式转化为后缀表达式。具体规则可以参考前一条的两个超链接的资料,或者参考视频动画:中缀转后缀。(PS:个人认为本题的最大难点就是在于这个步骤,这个步骤可能算不上特别难,但是里面有许多小细节需要注意,不然会导致转换错误)
- 转换中缀表达式过程中,需要将运算符存入栈中,这需要先对字符的优先级进行比较,由于运算符较多,因此我设置了一个函数,给操作符设置了一个整型的优先级,以便于进行对比。
- 将中缀表达式转换为后缀表达式以后,问题就变得较为容易,可以按照逆波兰表达式的计算规则对表达式进行处理,最终可以得到运算结果。
- 由于对逆波兰表达式进行计算时还需要将操作数存入栈中,我个人觉得太啰嗦就通过数组实现了一个‘假栈’来储存操作数。(本题两次用到栈,且存储的数据的类型不同,一个是字符型一个是整型,由于水平有限,我无法实现栈的复用,就采取了这样的方式)
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Stack* S;
typedef char type;
struct Stack
{
type data[100];
int pos;
};
//创建并初始化堆栈
S init()
{
S s;
s = (S)malloc(sizeof(struct Stack));
s->pos = -1;
return s;
}
//判断栈是否为空
int empty(S s)
{
if (s->pos == -1)
return 1;
return 0;
}
//压栈
int push(S s, type x)
{
s->pos += 1;
s->data[s->pos] = x;
return 0;
}
//弹栈
type pop(S s)
{
s->pos -= 1;
return s->data[s->pos + 1];
}
//获得运算符的优先级
int getgrade(char c)
{
if (c == '(')
return 1; //由于栈顶为左括号时符号可直接压栈,因此将其等级设为最低
if (c == '+' || c == '-')
return 2;
if (c == '*' || c == '/')
return 3;
}
//主函数
int main()
{
char str[100] = { '\0' }, fin_str[100] = { '\0' }; //储存初始字符串、逆波兰式
int operand[100] = { 0 }, i = 0, j = 0, pos_ope = -1;
S s = init();
gets_s(str);
while (str[i] != '=') {
if (str[i] == ' '){ //空格不处理
i++;
continue;
}
if (str[i] >= '1' && str[i] <= '9') { //数字直接添加到逆波兰表达式,通过逗号分隔
while (str[i] >= '0' && str[i] <= '9') {
fin_str[j++] = str[i++];
}
fin_str[j++] = ',';
continue;
}
else if (empty(s) || str[i] == '(') { //空栈或者栈顶为左括号时直接将运算符压栈
push(s, str[i++]);
continue;
}
char c = pop(s);
if (str[i] == ')') { //运算符为右括号时,开始弹栈,直到弹出左括号
while (c != '(') {
fin_str[j++] = c;
fin_str[j++] = ',';
c = pop(s);
}
i++;
}
else { //栈顶与当前字符都是普通运算符时,如果当前字符优先级小,则弹栈,直到其比栈顶元素优先级高,再压栈
while (getgrade(c) >= getgrade(str[i])) {
fin_str[j++] = c;
fin_str[j++] = ',';
if (empty(s)) //若把栈弹空,则跳出循环
break;
c = pop(s);
}
if (getgrade(c) < getgrade(str[i])) { //若由于弹出的栈顶元素优先级小于当前字符而跳出上方循环,则应将其再压栈
push(s, c);
push(s, str[i++]);
}
}
}
while (!empty(s)) { //处理完字符串,应将栈内剩余所有运算符都添加到逆波兰表达式
fin_str[j++] = pop(s);
fin_str[j++] = ',';
}
i = 0;
while (i<strlen(fin_str)) {
if (fin_str[i] == ','){ //解析逆波兰表达式,作为分隔符的逗号不解析
i++;
continue;
}
char c = fin_str[i];
if (c == '+' || c == '-' || c == '*' || c == '/') { //如果是运算符,就在操作数栈中弹出两个元素进行相应运算
int b = operand[pos_ope--], a = operand[pos_ope--], res;
switch (c){
case '+':res = a + b; break;
case '-':res = a - b; break;
case '*':res = a * b; break;
case '/':res = a / b; break;
}
operand[++pos_ope] = res; //将运算结果重新压入运算符栈
i++;
}
else{ //解析到数字时,将其由字符串形式转为整数,压入操作数栈
char ope[5] = { '\0' };
j = 0;
while (fin_str[i] >= '0' && fin_str[i] <= '9') {
ope[j++] = fin_str[i++];
}
int res = atoi(ope); //stdlib.h自带函数,将数字字符串转为整数
operand[++pos_ope] = res;
}
}
printf("%d\n", operand[0]); //操作数栈内最终剩下的元素即运算结果
return 0;
}