实验6 栈——算术表达式
注意
因为精度问题,请使用double存数据。
要求
- 创建栈类,采用数组描述;
- 计算数学表达式的值。
输入数学表达式,输出表达式的计算结果。数学表达式由单个数字和运算符+
、-
、*
、/
、(
、)
构成,例如2+3*(4+5)-6/4
。假定表达式输入格式合法。
格式
输入
第一行一个整数n(1<=n<=100),代表表达式的个数。
接下来n行,每行一个表达式,保证表达式内的数字为单个整数,表达式内各运算符和数字间没有空格,且表达式的长度不超过2000。
输出
每行表达式输出一个浮点数,要求保留两位小数,保证输入表达式合法。
思路与探讨
栈
整体思路描述
-
首先定义一个数组栈,定义并实现构造函数、出栈、入栈的相关函数;
-
然后定义运算,
原理即将数字栈的前两个数和符号栈栈顶元素取出做一次运算,并将结果压入数字栈
; -
最后定义功能函数,即实现字符串读取,识别数字和字符并进行相关运算。
细节思路补充
-
运算符读取时关注
先乘除后加减
-
负数判断?
开一个 judge 标记。识别符号,将 judge 标记为 true。再读取该符号后的数值后对其作取负操作,具体如下:
if((i == 0||s[i-1] == '(')&& s[i] =='-') { judge = true;//负数标记 continue; }
if(judge) { value = -value;//负数处理 judge = false;//变回来,之后遇到"-"再次变换 }
-
多位数处理?
while(s[i+1] >= '0' && s[i+1] <= '9') { value = 10 * value + (s[i+1]-'0'); i++; }
若已看懂思路,试着自己写~
实现代码
#include<iostream>
#include<string>
#include <iomanip>
using namespace std;
//定义一个数组栈
template<class T>
class arraystack
{
public:
arraystack(int initialCapacity = 10);//构造函数
~arraystack(){delete [] stack;}//析构函数
bool empty() const {return stackTop == -1;}
int size() const {return stackTop + 1;}
T& top()
{
if(stackTop == -1)
exit(1);
return stack[stackTop];
}
void pop();//出栈
void push(const T& theElement);//入栈
private:
int stackTop;//当前栈顶
int stackLength;//栈容量
T *stack;//元素数组
};
template<class T>
arraystack<T>::arraystack(int initialCapacity)
{//构造函数
if(initialCapacity < 1)
exit(1);
stackLength = initialCapacity;
stack = new T[stackLength];
stackTop = -1;
}
template<class T>
void arraystack<T>::pop()
{//出栈
if(stackTop == -1)
exit(1);
stack[stackTop--].~T();//T的析构函数
}
template<class T>
void arraystack<T>::push(const T& theElement)
{//入栈
if(stackTop == stackLength - 1)
{//容量满后的容量扩充
stackLength *= 2;//将栈的容量扩充二倍
T newStack[stackLength];
for(int i = 0;i < stackLength;i++)
{
newStack[i] = stack[i];
}
delete stack;
stack = newStack;
}
stack[++stackTop] = theElement;
}
//定义运算
//原理:将数字栈的前两个数和符号栈栈顶元素取出做一次运算,并将结果压入数字栈
void calculate(arraystack<double> &number, arraystack<char>&symbol)
{
double a,b;
//取出数字栈的前两个数
a = number.top();//取出数字栈第一个数
number.pop();//弹出栈顶元素,这样才能取第二个
b = number.top();//取出当前数字栈第一个数
number.pop();//弹出栈顶元素
//取符号栈栈顶元素,且根据符号栈的栈顶元素不同进行不同运算
switch(symbol.top())
{
case '+':
number.push(a+b);
break;
case '-':
//算术里先读取的在下方,所以是b-a
number.push(b-a);
break;
case '*':
number.push(a*b);
break;
case '/':
//算术里先读取的在下方,所以是b/a
number.push(b/a);
break;
default:break;
}
symbol.pop();//将运算完的符号弹出栈
}
void function(string s,int length)
{//功能实现
bool judge = false;
arraystack<double> number(length);//声明一个数字栈
arraystack<char> symbol(length);//声明一个字符栈
for(int i = 0;i < length;i++)//遍历输入的表达式
{
if(s[i] == ' ') continue;//如果输入的如果是空格,跳过
if(s[i] >= '0'&&s[i] <= '9')
{//读到的数字
int value = s[i]-'0';//把字符转化为运算数值
while(s[i+1] >= '0'&&s[i+1] <= '9')
{//多位数处理
value = 10 * value + (s[i+1]-'0');
i++;
}
if(judge){
value = -value;//负数处理
judge = false;//变回来,之后遇到"-"再次变换
}
number.push(value);
}
else
{//读到的是字符
if((i == 0||s[i-1] == '(')&& s[i] =='-')
{
judge = true;//负数标记
continue;
}
switch(s[i])
{
//加减运算
case '+':
case '-':
if(symbol.empty()||symbol.top() == '(') symbol.push(s[i]);
else
{
//不管那个字符是啥,先算了,让那个字符pop掉
calculate(number,symbol);
//pop掉一个可能还有一个,继续calculate,把已经有的能运算的先用了
if(!symbol.empty() && symbol.top() != '(') calculate(number,symbol);
symbol.push(s[i]);
}
break;
//乘除运算
case '*':
case '/':
if(symbol.empty()||symbol.top() == '(') symbol.push(s[i]);
//先乘除,后加减
else if(symbol.top() == '+'||symbol.top() == '-') symbol.push(s[i]);
else if(symbol.top() == '*'||symbol.top() == '/')
{
calculate(number,symbol);
symbol.push(s[i]);
}
break;
case '('://左括号直接入栈
symbol.push(s[i]);
break;
case ')'://遇到右括号 ,对括号内运算直至运算到左括号
while (symbol.top()!='(')
{
calculate(number,symbol);
}
symbol.pop();//将左括号弹出栈
break;
default:break;
}
}
}
while(!symbol.empty()) calculate(number,symbol);
cout<<setprecision(2)<<fixed<<number.top()<<endl;//精度控制,保留两位小数
}
int main()
{
int n;
cin >> n;
for(int i = 0;i < n;i++)
{
//将输入的表达式存到一个字符串中
string str;
//记录字符串长度
int length;
cin >> str;
length = str.length();
function(str,length);
}
return 0;
}