表达式求值
#中缀表达式、后缀表达式
#表达式求值之后缀表达式的应用(不允许输入负数)
具体分为两步,第一步求后缀表达式,第二步是利用后缀表达式求解。
前言
表达式求值也有一种思路清晰的算法,但是有个前提,就是输入的数据中,不能有负数。
我最讨厌这种字符串来计算有关数学运算的问题了,需要在短时间内考虑太多的情况,做不好的话,容易产生挫败感。
字符串的问题,掌握一定的算法和基础,在实际的问题中还是要根据所学过的方法和个人的分析问题的能力去解决此类问题的,believe yourself!
好了好了,发发牢骚,开始分析题目。
中缀表达式、后缀表达式
中缀表达式: 我们平常见到的表达式都是称为中缀表达式,如:(3+4) * 5-6
在讲解这个算法之前,我们要明白什么是后缀表达式
那什么是后缀表达式呢?
后缀表达式: 后缀表达式,又称逆波兰式,运算符在操作数之后的表达式。(但不是纯粹的运算符在操作数之后,一会举例说明。)
例:(3+4) * 5-6的后缀表达式是如何表示的呢?
答:34+5*6-
具体怎么求的,往下看。
把中缀表达式转化为后缀表达式
中缀表达式转后缀表达式方法:
- 定义两个栈,s1存操作数,s2存运算符。
- 对给定的表达式字符串,从左往右扫描,如果是操作数,入s1栈。
- 如果是运算符:
- 如果s2为空,或者s2栈顶是’(’,或者当前运算符的优先级大于 s2栈顶元素的优先级,入s2栈。
- 否则,当s2栈不空且当前运算符的优先级小于等于s2栈顶的元素的优先级,取s2栈顶元素入s1栈,重复上一步的操作。
- 如果是括号:
- 如果是’(’,入s2栈。
- 如果是’)’,
- 取s2栈顶元素进行判断
- 若不是’(’,则加入s1栈,循环上一步。
- 若是’(’,则出栈,结束循环。
- 取s2栈顶元素进行判断
- 直至遍历到表达式右侧。
- 把s2中的元素倒序输出,即为后缀表达式。
利用后缀表达式求解
数字是两位以上的数怎么办?
因为有两位以上的数参与运算,又因为输入的表达式是一串字符串,所以,在收集两位数的值的时候需要使用一些技巧,在求后缀表达式的时候,遍历字符串表达式:
- 当前的字符是数字且下一个也时数字且 下一个数字的下标不超过数组的长度,
s1.push(x-48);
//只添加一个数到栈中, - 否则,
s1.push(x-48); s1.push('#');
//这里加了一个#作为每段数字的分隔符
利用后缀表达式求解
1 定义一个整型栈 s,
2 给定后缀表达式的栈s2,从左往右遍历,如果是一段字符数,处理成整数字后,入栈s,如果是运算符,从s栈中取两个操作数,(次栈顶元素op栈顶元素),结果放入栈s中,直至表达式遍历结束。
3 输出栈中元素,即为结果。
代码:
//表达式求值
#include<stdio.h>
#include<string.h>
#include<stack>
#include<string>
#include<math.h>
#include<iostream>
using namespace std;
//设置一个优先级判断的函数
int priority(char x){
if(x=='/'||x=='*')
return 2;
else if(x=='+'||x=='-')
return 1;
else
return 0;
}
int main(){
stack<char> s1,s2;
string str;
cin>>str;
//转后缀表达式
//从左往右扫描表达式串
//s1 操作数栈 s2 运算符栈
int len=str.size();
for(int i=0;i<len;i++){
if(str[i]>=48&&str[i]<=57){//如果是数
if(str[i+1]>=48&&str[i+1]<=57&&i+1<len)
s1.push(str[i]);
else{
s1.push(str[i]);
s1.push('#');
}
}else if(str[i]!='('&&str[i]!=')'){//如果是运算符(不是括号)
if(s2.empty()||s2.top()=='('||priority(str[i])>priority(s2.top())){
s2.push(str[i]);
}else{
while(!s2.empty()&&priority(str[i])<=priority(s2.top())){//注意这个括号
s1.push(s2.top());
s2.pop();
}
s2.push(str[i]);
}
}
else{
if(str[i]=='(')
s2.push(str[i]);
else{
while(s2.top()!='('){//这一步去除括号了
s1.push(s2.top());
s2.pop();
}
s2.pop();
}
}
}
//全放入s1中
while(!s2.empty()){
s1.push(s2.top());
s2.pop();
}
//做逆序处理成后缀表达式
while(!s1.empty()){
s2.push(s1.top());
s1.pop();
}
//-------------------------------------------
//这个时候顶一个一个int型的结果栈,以用来存放结果
//再定义一个数组,计算两位以上的数
int num[110];
stack<int> s;
while(!s2.empty()){
int k=0,sum=0;
if(s2.top()>=48&&s2.top()<=57){
while(!s2.empty()&&s2.top()>=48&&s2.top()<=57){
num[k++]=s2.top()-48;
s2.pop();
}
for(int i=0;i<k;i++)
sum+=num[i]*pow(10,k-i-1);
s.push(sum);
}else if(s2.top()=='#'){
s2.pop();
}else{
//次栈顶元素操作栈顶元素
int num1=s.top();
s.pop();//栈顶元素
int num2=s.top();
s.pop();//次栈顶元素
if(s2.top()=='+')
s.push(num2+num1);//存进去的是acsii码
else if(s2.top()=='-')
s.push(num2-num1);
else if(s2.top()=='*')
s.push(num2*num1);
else
s.push(num2/num1);
s2.pop();
}
}
//s1的栈顶就是结果
cout<<s.top()<<endl;
}
补充的内容(前缀表达式)
(不用看,感兴趣的话了解一下,写给自己看的,如果怕与上面的知识混淆,建议不看)
前缀表达式: 前缀表达式,又称为波兰式,运算符在操作数之前的表达式。(但不是纯粹的运算符在操作数之前,一会举例说明。)
转前缀表达式:
- 定义两个栈,一个s1存操作数,一个s2存运算符。
- 从右往左扫描,如果是操作数,入s1栈。
- 如果是运算符:
- 如果栈为空,或者栈顶元素为’)’,或者当前的运算符的优先级大于等于栈顶元素的优先级,入s2栈。
- 否则,当s2栈不空,取s2栈顶元素入s1栈,重复上一步操作。
- 如果是括号:
- 如果是’)’,入s2栈。
- 如果是’(’,
- 取s2栈顶元素
- 若不是’)’,则加入到s1栈
- 若是’)’,则出栈,结束循环。
- 直至遍历到表达式左侧。
- 把s2中的元素顺序输出,即为前缀表达式。