目录
后缀表达式
后缀表达式:运算符号位于两个运算数之后。如, a b c + * d e / -
应用堆栈实现后缀表达式求值的基本过程:
从左到右读入后缀表达式的各项(运算符或运算数);
- 运算数:入栈;
- 运算符:从堆栈中弹出适当数量的运算数,计算并结果入栈;
- 最后,堆栈顶上的元素就是表达式的结果值。
后缀表达式求值的主体函数
//后缀表达式求值
double evaluteH(string expression)
{
stack<double> num;
double sum=0;
vector<string> s = scan(expression);//即对象分割
int size = s.size();
for(int i=0;i<size;i++)
{
if(isdigit(s[i][0])){
num.push(atof(s[i].c_str()));
}
else
{
double t2 = num.top();
num.pop();
double t1 = num.top();
num.pop();
sum = cal(t1,t2,s[i][0]);
num.push(sum);
}
}
return sum;
}
其它辅助函数
对象分割函数scan()
//经过扫描,scan将输入的字符串中操作数和操作符进行了分离
//即抽取expression中的数字、运算符和括号,将抽取结构储存在字符串向量v中
//例如,expression:(13+2)*4-3;则返回结果为:(,13,+,2,),*,4,-,3
vector<string> scan(string a)
{
vector<string> v;//用string类型向量v来储存处理后 数字、运算符和括号,
//这样巧用string ,会使后续运算更加方便
string numString;//用来储存数字
int len = a.size();
for(int i=0;i<len;i++)//逐个扫描
{
if(isdigit(a[i]) || a[i] == '.')//如果是数字或小数点,存入numString中
{
numString.append(1,a[i]);
}
else
{
if(numString.size() >0 )
{
v.push_back(numString);
//如果不是数字说明,numString已储存一个完整的操作数一个操作数
//把记录的数字压入v中
numString.erase();//更新numString
}
if(!isspace(a[i]))//不是空格符,就是运算符
{
string s;
s.append(1,a[i]);
v.push_back(s);//把运算符推入v中
}
}
}
if(numString.size() > 0)
{v.push_back(numString); } //把最后一个数字压入v中
return v;
}
进行一次计算的函数cal()
double cal(double a,double b,char op)
{
switch (op)
{
case '+':return a + b;
case '-':return a - b;
case '*':return a * b;
case '/':return a / b;
case '%':return (int)a % (int)b;
case '^':return pow((int)a,(int)b) ;
default:cout << "error !";
}
}
中缀表达式
中缀表达式:运算符号位于两个运算数之间。如 ,a + b * c - d / e
求值思路:
转换为后缀表达式求值:
思路一——转换完再求解
将中缀表达式的字符序列转换为后缀表达式的字符序列,再配合本文上面后缀表达式的求值函数进行求值。
转换函数string change(string expression);如下:
//将中缀表达式的字符序列转换为后缀表达式
string change(string expression)
{
string v="";
string numString;//为使输出的两操作数之间有空格,用numString分割每一个单独的操作数
stack<char> op;//操作符临时栈
int len = expression.length();
for(int i=0;i<len;i++)
{
if(isdigit(expression[i]) || expression[i] == '.'|| isalpha(expression[i]) )
{
numString.append(1,expression[i]);
}
else
{
if(numString.size() > 0)
{
v = v + numString + " ";//加一个空格
numString.erase();
}
}
//特别注意,括号不能出现在后缀表达式中
if(!isspace(expression[i])&&!isdigit(expression[i])&&!isalpha(expression[i]))
{ if(!op.empty())
{ char op1 = op.top();
char op2 = expression[i];
if(op2 == ')')
{
while(op1 != '(')
{
v.append(1,op1);
v += " ";
op.pop();
op1 = op.top();
}
op.pop();
}
else
{
while(!op.empty() &&priority(expression[i]) <= priority(op.top())&&op.top()!='(')
{
v.append(1,op.top());
v += " ";
op.pop();
}
op.push(expression[i]);
}
}
else
op.push(expression[i]);
}
}
//别忘了清楚最后留在numString和操作数栈中的操作数或操作符
if(numString.size() > 0)
{
v = v + numString + " ";//加一个空格
numString.erase();
}
while(!op.empty())
{ if(op.top() != '(')
{
v.append(1,op.top());
v += " ";
}
op.pop();
}
return v;
}
优先级函数----int priority(char op);
其中优先级比较运用了int priority(char op);函数
//返回各符号的优先级,用数字表示,便于判断
int priority(char op)
{
if(op == ')')
return 0;
else if(op == '+'||op == '-')
return 1;
else if(op == '*'||op == '%'||op == '/')
return 2;
else if(op == '^')
return 3;
else if(op == '(')
return 4;
else
{
cout << "error";
return -1;
}
}
思路二——边转化边求解
这里的符号优先级是用多重暴力讨论来解决,也并不复杂
主要函数
double evalute1(string expression)
{
stack<double> num;
stack<char> op;
vector<string> s = scan(expression);
//经过扫描,s将输入的字符串中操作数和操作符进行了分离
//即抽取expression中的数字、运算符和括号,将抽取结构储存在字符串向量s中
//例如,expression:(13+2)*4-3;则s为:(,13,+,2,),*,4,-,3
int size = s.size();
for(int i=0;i<size;i++)
{
if(s[i][0] == '+' || s[i][0] == '-')
{
while(!op.empty() && op.top() != '(')
{
cal(num,op);
}
op.push(s[i][0]);
}
else if(s[i][0] == '*' ||s[i][0] == '/'||s[i][0]=='%')
{
while(!op.empty() && (op.top() == '%' ||op.top() == '*' || op.top() == '/'||op.top() == '^'))
{
cal(num,op);
}
op.push(s[i][0]);
}else if(s[i][0] == '^'){
op.push(s[i][0]);
}
else if(s[i][0] == '('){
op.push(s[i][0]);
}else if(s[i][0] == ')')
{
while(op.top() != '(')
{
cal(num,op);
}
op.pop();
}else
{
num.push(atof(s[i].c_str())) ;//调用c_str,atof()将字符串转浮点型数字,
}
}
while(!op.empty()){
cal(num,op); }
return num.top();
}
scan()扫描分离函数同上所示
//经过扫描,scan将输入的字符串中操作数和操作符进行了分离
//即抽取expression中的数字、运算符和括号,将抽取结构储存在字符串向量v中
//例如,expression:(13+2)*4-3;则返回结果为:(,13,+,2,),*,4,-,3
vector<string> scan(string a)
{
vector<string> v;//用string类型向量v来储存处理后 数字、运算符和括号,
//这样巧用string ,会使后续运算更加方便
string numString;//用来储存数字
int len = a.size();
for(int i=0;i<len;i++)//逐个扫描
{
if(isdigit(a[i]) || a[i] == '.')//如果是数字或小数点,存入numString中
{
numString.append(1,a[i]);
}
else
{
if(numString.size() >0 )
{
v.push_back(numString);
//如果不是数字说明,numString已储存一个完整的操作数一个操作数
//把记录的数字压入v中
numString.erase();//更新numString
}
if(!isspace(a[i]))//不是空格符,就是运算符
{
string s;
s.append(1,a[i]);
v.push_back(s);//把运算符推入v中
}
}
}
if(numString.size() > 0)
{v.push_back(numString); } //把最后一个数字压入v中
return v;
}
计算函数cal()则有所不同
//在操作符栈中取栈顶元素,在操作数栈中取头两个元素,
//然后计算,并将计算结果推入操作数栈
double cal(stack<double>& num,stack<char>& op)//要传引用,否则会出错
{
char ch = op.top();
op.pop();
double b = num.top();
num.pop();
double a = num.top();
num.pop();
switch (ch)
{
case '+':num.push(a + b);return a + b;
case '-':num.push(a - b);return a - b;
case '*':num.push(a * b);return a * b;
case '/':num.push( a / b);return a / b;
case '%':num.push(fmod(a,b));return fmod(a,b);//取余操作,调用fmod()实现浮点数取余
case '^':num.push(pow((int)a,(int)b));return pow((int)a,(int)b) ;
default:cout << "error !";
}
}
测试结果如下所示
支持整数浮点数,加减乘除,取余,乘方运算
啃完了后缀,中缀表达式,最后奉上前缀表达式的求值方式放松放松
前缀表达式
逆波兰表达式的定义:
1)一个数是一个逆波兰表达式,值为该数
2)"运算符 逆波兰表达式 逆波兰表达式”是逆波兰表达式,值为两个逆波兰表达式的值运算的结果
思路
不需要考虑运算优先级,用递归,直接上代码
//读入一个逆波兰表达式,并计算其值
double exp()
{
char s[20];
cin >> s;
switch (s[0])
{
case '+' :return exp() + exp();
case '-' :return exp() - exp();
case '*' :return exp() * exp();
case '/' :return exp() / exp();
default :return atof(s);//不是运算符,就是数,所以将s转化为数字
}
}
int main(){
cout<<exp();
}