表达式的三种形式:
中缀表达式:运算符放在两个运算对象中间,这就是我们数学课本上一直使用的形式,如:(2+1)*3
后缀表达式:不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则,如:2 1 + 3 *
前缀表达式:同后缀表达式一样,不包含括号,运算符放在两个运算对象的前面,如:* + 2 1 3,注意 / 2 1 对应的 中缀表达式是 2 / 1 而不是 1 / 2。
由于前缀表达式不常用
这里就给大家讲解一下如何用栈来实现中缀表达式向后缀表达式的转换。
算法步骤:
假设我们欲将中缀表达式
a + b * c + ( d * e + f ) * g
转换成后缀表达式 。正确的答案是
a b c * + d e * f + g * +
算法总的来说可以分为4步:
①当读到字母时,我们直接输出,当读到运算符时,我们将运算符推入栈。(当是空栈的时候)
②当读到右括号”)“,我们就将栈中的元素弹出并输出,直到我们遇到对应的左括号,但是这个左括号只被弹出,并不输出。
③当栈中有元素的时候,如果我们又遇到了运算符{ ” + “ , ” - “ ” x “ ” / “ " ( " },那么我们从栈中弹出元素并输出, 直到我们发现优先级更低的元素为止。 这里有一个例外,除非遇到 右括号“)”,否则我们绝不将 左括 号”(“弹出。
对于这个操作, ” +“ ” - “ 的优先级最低, ” x “ ” / “其次, ”(“的优先级最高 。 当我们从栈中弹出元素的任务完成的时候,我们再将遇到的运算符压入栈中。
④最后,如果我们读到输入的末尾,我们将栈中的元素依次弹出直到变为空栈,并依次输出栈中的元素。
这里给出对应中缀表达式转后缀表达式的源码,输入字符,以 # 结束。
#include<iostream>
#include<cstdio>
#include<malloc.h>
#include<stack>
#include<cctype>
#include<map>
using namespace std;
typedef char ELementType;
const int INIT_SIZE = 100;
struct Stack{ //顺序储存构造栈
int top;
int weight;
ELementType *St;
int sz;
Stack(){
top = 0;
weight = 1;
St = (ELementType*)malloc(INIT_SIZE*sizeof(ELementType));
sz = INIT_SIZE;
}
void push(int x){
if(top<sz)
St[top++] = x;
else{
weight ++;
sz = INIT_SIZE*weight;
St = (ELementType*)realloc(St,INIT_SIZE*weight*sizeof(ELementType));
St[top++] = x;
}
}
void pop(){
St[top-1] = 0;
top--;
}
int Top(){
return St[top-1];
}
bool empty(){
if(top == 0)
return true;
return false;
}
};
Stack st;
void print(){ //打印栈中剩余的元素
while(!st.empty()){
char tmp = st.Top();
st.pop();
printf("%c",tmp);
}
printf("\n");
}
map<char,int>mp;
int main(){
mp['+'] = 1;mp['-'] = 1; //用 map 构造优先级
mp['*'] = 2;mp['/'] = 2;
mp['('] = 3; mp[')'] = 4;
char ch;
while(ch = getchar()){
if(ch == '#')
break;
if(isalpha(ch)) //如果是字符直接输出
printf("%c",ch);
else{
if(st.empty()){ //如果栈空 推入运算符
st.push(ch);
}
else{
if(mp[st.Top()] >= mp[ch]){ //如果栈顶元素的优先级大于等于 当前运算符的优先级
while(!st.empty() && st.Top() != '(' && mp[st.Top()] >= mp[ch]){ //遇到左括号为止
printf("%c",st.Top());
st.pop();
}
st.push(ch); //推入当前运算符
}
else{
if(ch == ')'){
while(!st.empty() && st.Top() != '('){
printf("%c",st.Top());
st.pop();
}
st.pop(); //删除左括号
}
else
st.push(ch);
}
}
}
}
print(); //打印栈中剩余的运算符
return 0;
}
计算后缀表达式
由于后缀表达式没有括号,而且并不需要知道任何优先规则,所以计算机处理起来比较方便,而且可以实现线性效率的运算 时间花费为O(n),
这里继续用栈来实现后缀表达式的求值。
a b c * + d e * f + g * +
a + b * c + ( d * e + f ) * g
这个算法非常简单:
和中缀表达式转后缀表达式正好相反。 遇到字母我们推入栈中, 遇到运算符, 我们就把栈中最上面两个元素弹出来,进行运算,再将运算结果再推入栈中。
注意如果运算符是 / 我们要用第二个弹出的数 除以 第一个弹出的数。
最后的结果就是栈中剩余的最后一个元素。
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<stack>
using namespace std;
const int maxn = 110;
stack<int>st;
int main(){
char ch;
while(ch = getchar()){
if(ch == '#') {
int fans = st.top();
st.pop();
printf("%d\n",fans);
break;
}
if(isdigit(ch)){
int tmp = ch-'0';
st.push(tmp);
}
else{
int a = st.top();
st.pop();
int b = st.top();
st.pop();
int ans;
if(ch == '+')
ans = b + a;
else if(ch == '-')
ans = b - a;
else if(ch == '*')
ans = b * a;
else if(ch == '/')
ans = b / a;
// cout<<ans<< " " <<ch<<endl;
st.push(ans);
}
}
return 0;
}
当然,上面的两个算法只是纸上谈兵,数据结构的课程。第二个计算后缀表达式的方法也只能计算 个位数的多项式运算,因此这里我在给出浮点数带括号的多项式计算器。
综合了上面两个算法。
#include<cstdio>
#include<algorithm>
#include<vector>
#include<iostream>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#define INF 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
double change(string s,int st,int end){ //字符串转换成浮点数
double ans = 0; double l = 1;int po = -1;
for(int i=end-1;i>=st;i--) //找到小数点
if( s[i] == '.') {po = i ;break;}
if(po != -1)//找到小数点,则l右移
for(int i=0;i<end-1-po;i++)
l/=10;
for(int i=end-1;i>=st;i--){
if(s[i] == '.') continue;
ans += (s[i] - '0') * l;
l*= 10;
}
return ans;
}
int main(){
map<char,int> prio; //记录优先级
map<int,double> intToDouble; //浮点数映射
prio['+'] = 1; prio['-'] = 1;
prio['*'] = 2;prio['/'] = 2;
prio['('] = 3; prio[')'] = 5;
stack<char>st;
char ans[1010]; int po = 0; //记录数组长度,作下标
double numVal[1001];int l = 0; //映射
string polynomial;
cin>>polynomial;
for(int i=0;i<polynomial.length();i++){
char ch = polynomial[i];
if(isdigit(polynomial[i])){
int j;
for(j=i+1;j<polynomial.length();j++){
if(!isdigit(polynomial[j]) && polynomial[j] != '.' ) break;
}
double num = change(polynomial,i,j);
intToDouble[l] = num;
ans[po++] = l; //储存数据的数组
l++;
i = j - 1; // i 跳转到 对应位置
continue;
}
else{ //标准的中缀转后缀方法
if(st.empty()){
st.push(ch);
}
else{
if(prio[st.top()] >= prio[ch]){
while(!st.empty() && st.top()!='(' && prio[st.top()] >= prio[ch]){
ans[po++] = st.top(); st.pop();
}
st.push(ch);
}
else{
if(ch == ')'){
while(!st.empty() && st.top() != '('){
ans[po++] = st.top(); st.pop();
}
st.pop(); // delete '('
}
else
st.push(ch);
}
}
}
}
while(!st.empty()){
ans[po++] = st.top(); st.pop();
}
ans[po] = '\0';
//cout<<ans<<endl;
//标准的计算后缀表达式方法
stack<double>st2;
for(int i=0;i<po;i++){
if(intToDouble.find(ans[i])!=intToDouble.end()){
st2.push(intToDouble[ans[i]]); //将 映射之后的数推入数组
}
else{
char ch = ans[i];
double a = st2.top(); st2.pop();
double b = st2.top(); st2.pop();
double ans ;
if(ch == '+') ans = b+a;
if(ch == '-') ans = b-a;
if(ch == '*') ans = b*a;
if(ch == '/') ans = b/a;
st2.push(ans);
}
}
cout<<"= "<<st2.top()<<endl; //最后剩下的数就是答案
st.pop();
return 0;
}
不过程序的鲁棒性比较差,必须保证正确输入多项式
转载请注明出处。