简单计算器的实现
一:问题描述:
写一个简单的计算器,计算一个表达式。
已知:
1.用string存储这个表达式。
2.这个表达式有'(',')','+','-'以及空格和大数运算。
3.可以认为输入是一定正确的,及不用判断其输入格式。
输入案例:
"1 + 1" = 2
" 2-1 + 2 " = 3
"(1+(4+5+2)-3)+(6+8)" = 23
注意: 不得使用eval库.
二:算法解析
1.这道题的算法核心是将表达式解析成逆波兰式(或波兰式)以便电脑识别计算,这里将解析为逆波兰式。
2.大数的加减运算,这里省略。
注:逆波兰表示法(Reverse Polish notation,RPN,或逆波兰记法),是一种数学表达式方式,在逆波兰记法中,
所有操作符置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级
解析成逆波兰式的步骤:
1.先生成s1,s2两个栈,其中s1用来存贮运算符,s2用来存贮解析出的逆波兰式。并且将'#'压入s1栈中,'#'可以用处运算符外任意其他字符替代,其作用是作为一个优先级最小的符号标
记存贮在s1中,后面的步骤你将看到其作用。
2.逐个解析string
3.若x是操作数,则解析出完整的操作数,并将x直接压入栈s2;
若x是运算符,则分情况讨论:
若x是'(',则直接压入栈s1;
若x是')',则将距离栈s1栈顶的最近的'('之间的运算符,逐个出栈,依次压入栈s2,并pop掉'(';
若x是'+或'-'或其他除'('和')'的运算符,则分情况讨论:
若当前栈s1的栈顶元素为'(',则将x直接压入栈s1;
若当前栈s1的栈顶元素不为'(',则将x与栈s1的栈顶元素比较,若x的优先级大于栈s1栈顶运算符优先级,则将x直接压入栈s1。否者,
将栈s1的栈顶运算符弹出,压入栈s2中,直到栈s1的栈顶运算符优先级别低于(不包括等于)x的优先级,或栈s2的栈顶运算符为'(',此时再则将x压入栈s1;
4.重复2,3操作。
5.在解析完string后,检查栈s1是否为空,若不为空,则将栈中元素依次弹出并压入栈s2中(不包括'#')。
6.最终s2中就存储了表达式的逆波兰式。
三:就题论题
由于这道题不是仅仅求其逆波兰式,而是计算其值。故可如下操作:
在解析逆波兰式的过程中,每向栈s2中添加运算符时,判断其是哪个运算符,此时不用再将其添进s2中,而是可以对s2中就接近栈顶的两操作数直接运算,并将两个操作数出栈,将其结果进栈。由于表达式的正确性确定,故在所有操作结束时,s2中仅留下一个操作数,就是其算式的结果,输出即可。
四:核心代码(栈的构造与解析)
s1.push('#');
int length = s.length(), i = 0;
//转化为数字接受变量
long long int number = 0;
bool num = false;
char ch;
while(i != length)
{
switch(s[i])
{
case ' ':
break;
case '(':
//符号前面是否有数字
if(num)
{
s2.push(number);
num = false;
number = 0;
}
s1.push(s[i]);
break;
case ')':
//符号前面是否有数字
if(num)
{
s2.push(number);
num = false;
number = 0;
}
while(s1.top() != '(')
{
char ch = s1.top();
//figure
figure(s2, ch);
s1.pop();
}
s1.pop();
break;
case '+':
case '-':
//符号前面是否有数字
if(num)
{
s2.push(number);
num = false;
number = 0;
}
for(ch = s1.top(); ch != '#'; ch = s1.top())
{
if(ch == '(')
{
break;
}
//figure
figure(s2, ch);
s1.pop();
}
s1.push(s[i]);
break;
default:
//number将字符转换为数字
number = s[i] - '1' + 1 + number * 10;
num = true;
break;
}
i++;
}
if(num)
s2.push(number);
while(s1.top() != '#')
{
char ch = s1.top();
//figure
figure(s2, ch);
s1.pop();
}
return s2.top();
}