前缀,中缀,后缀表达式及其求值和转换

一.四则运算表达式

我们最常见的运算表达式是 : 12 * (3 + 4) - 6 + 8 / 2 这种类型,这就被称为是中缀表达式。

我们要用计算机来计算这一串表达式,然而表达式中涉及运算符的优先级顺序还有括号的干扰等,我们必须转化为一种易于计算的形式,于是这里就出现了后缀表达式和前缀表达式。

二.中缀转化后缀表达式及其运算

  20世纪50年代,波兰逻辑学家想到了一种不需要括号的后缀表达法,我们也把它称为逆波兰表示,所以后缀表达式又称为逆波兰表达式,中缀表达式转化后缀表达式的规则就是 运算符放置于两部分操作数之后。

例如:9+(3-1)*3+10/2   分为三部分  9 , (3-1)*3 , 10/2 

按照运算规则  :  [ 9 [(3-1)*3] + ] [10/2] +

继续由大部分到小部分看为一个整体拆分 : [ 9 [ (3-1) 3 * ] +] [10 2 /] +

继续:[9 [ (3 1 -)3 * ] +] [10 2 /] +

去括号 : 9 3 1 - 3 * + 10 2 / +  后缀表达式。

1. 后缀表达式的运算

(1) 由中缀转化后缀表达式的规则和过程可知: 越是关系密切的两个操作数,其之间的运算符一定紧跟它俩之后,运算完成的数字有可以作为一个操作数。所以这里我们使用栈来完成运算。

栈求值规则:

后缀表达式从前往后扫;遇到数字入栈;

遇到运算符,选择栈顶两元素出栈进行相应运算,将运算结果再入栈。继续扫

最终运算结果为栈顶元素

注:细节问题:<1>负数判断 <2>小数判断 <3> 带'+'的数字转化 <4>多位数字转化整数(123这样的数字)<5>不合规则的串判断

(2) 代码(整数版本)

void init(){
   len = 0;
   Priority['('] = 0;Priority['+'] = Priority['-'] = 1;Priority['*'] = Priority['/'] = 2;Priority['^'] = 3;//优先级
   while(!op.empty())op.pop();
   while(!num.empty())num.pop();
}
int calculate(int &now){//字符串转换数字,同时移动i(引用)
    int sum = 0;
    for(;now<len-1&&ans[now]>='0'&&ans[now]<='9';now++){
        sum*=10;
        sum+=(ans[now] - '0');
    }
    return sum;
}
int Union(int a,int b,char c){//运算
    if(c=='+')return a+b;
    else if(c=='-')return a-b;
    else if(c=='*')return a*b;
    else if(c=='/')return a/b;
    else return (int)(pow(a,b)+0.5);
}
void solve(){//求 9 3 1 - 3 * + 10 2 / + 的结果
   int i = 0;
   while(i<len-1){
      if(ans[i]==' '){//空格跳过(分割符号)
        i++;
        continue;
      }
      if(ans[i]=='-'&&ans[i+1]>='0'&&ans[i+1]<='9'){//处理负数
         i++;//跳到数字
         int sum = calculate(i);//抽出数字
         num.push(-sum);//入栈
      }
      else if(ans[i]>='0'&&ans[i]<='9'){//无符号整数
         int sum = calculate(i);
         num.push(sum);
      }
      else{//运算符
         int a = num.top();//抽出栈顶两元素运算
         num.pop();
         int b = num.top();
         num.pop();
         int sum = Union(b,a,ans[i]);
         //cout<<b<<ans[i]<<a<<" = "<<sum<<endl;
         num.push(sum);//入栈
         i++;
      }
   }
   printf("%d\n",num.top());//输出结果
}

2. 中缀表达式转化后缀表达式

(1)我们要明确以下运算规则:

<1>中缀表达式中的数字出现顺序在后缀表达式中不变

<2>先算乘除后算加减,右括号先算括号里的。所以按照后缀表达式的运算规则,优先级高的运算符肯定比相邻低运算符先放在前面,括号里的优先输出。

然后由上我们得到转化规则:

<1>中缀表达式从前往后扫,遇到数字就输出

<2>遇到运算符,先判断:

       若是' ( ' :则直接入栈

      若是 ' ) ' :把栈中直到 ' ( ' 之前的运算符全部输出,同时删掉' ( '

      若是其他运算符,若优先级高于栈顶元素,入栈。若优先级低于栈顶元素,则把栈中高于该元素的运算符全部输出,直到该        元素高于栈顶元素,入栈。

<3>最后,将栈中剩余运算符依次输出(优先级由高到低)

(2)代码(有小数):

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100000 + 7;
char str[maxn],ans[maxn];
int len,res;
stack<char> op;
map<char,int> Priority;
void init(){
   len = 0;
   Priority['('] = 0;Priority['+'] = Priority['-'] = 1;Priority['*'] = Priority['/'] = 2;Priority['^'] = 3;//优先级
   while(!op.empty())op.pop();
}
int Link(int st,int en){//抽离数字
   int i;
   for(i = st;i<en&&((str[i]>='0'&&str[i]<='9')||str[i]=='.');i++){//注意小数
       ans[len++] = str[i];
   }
   ans[len++] = ' ';
   return i;
}
void PopStack(){//')'出栈处理
    while(!op.empty()&&op.top()!='('){
         char ch = op.top();
         op.pop();
         ans[len++] = ch;
         ans[len++] = ' ';
    }
    op.pop();
}
void PushStack(char c){//运算符入栈规则处理
    while(!op.empty()&&Priority[op.top()]>=Priority[c]){
        char ch = op.top();
        op.pop();
        ans[len++]  = ch;
        ans[len++] = ' ';
    }
    op.push(c);
}
void TransForm(){
    int l = strlen(str);
    int i = 0;
    while(i<l){//从左往右扫
        if(str[i]>='0'&&str[i]<='9'){//遇到无符号整数
            i = Link(i,l);//抽出数字加空格
        }
        else if((str[i]=='-'&&(i==0||str[i-1]=='(')) || (str[i]=='+'&&(i==0||str[i-1]=='('))){//遇到负数或者带'+'的数字
            if(str[i]=='-')ans[len++] = str[i];//'+'不添加
            i++;
            i = Link(i,l);//抽出数字加空格
        }
        else if(str[i]=='('){//左括号入栈
            op.push(str[i]);
            i++;
        }
        else if(str[i]==')'){//右括号出栈
            PopStack();
            i++;
        }
        else{//其他运算符按照规则
            PushStack(str[i]);
            i++;
        }
    }
    while(!op.empty()){//剩余运算符
        char ch = op.top();
        op.pop();
        ans[len++] = ch;
        ans[len++] = ' ';
    }
    ans[len-1] = '\0';
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%s",str);
        TransForm();
        printf("%s\n",ans);
    }
    return 0;
}



//9+(3-1)*3+10/2

二.中缀转化前缀表达式及其运算

1.前缀表达式的计算求值

(1)前缀表达式的转化规则跟后缀表达式正好相反 :运算符放在两部分操作数之前。所以,优先级越高的运算符应该是越靠后的!所以这里,我们也是应该先运算优先级高的数字运算。因此,跟后缀运算恰恰相反 : 这里我们从后面往前扫!优先找到运算优先级高的!(因为是求值而已数字运算顺序无关)

运算规则:

<1>从后往前扫前缀表达式,遇到数字就入栈

<2>遇到运算符,选择栈顶两元素出栈进行相应运算,将运算结果再入栈。继续扫

<3>最终运算结果为栈顶元素

(2)代码(有小数,有符号数字):

#include <iostream>
#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
const int maxn = 100000 + 7;
stack<double> num;
char str[maxn];
bool judge;
double IsNum(int &s){
    double ans = 0;
    int tim = 1;//因为是从后往前抽数字,抽取方法应该是651 = 1 + 5*10 + 6*100依次类推
    bool flag = false;
    while(s>=0&&((str[s]>='0'&&str[s]<='9')||str[s]=='-'||str[s]=='.'||str[s]=='+')){
        if(str[s]=='.'){//遇到小数,当前积累的651就应该变成651/1000.0 = 0.651先,tim变回1
            if(s-1<0||str[s-1]<'0'||str[s-1]>'9'){//重新从个位积累
                return 0.0;//防止非法数字 .651出现
            }
            ans = ans/(tim*1.0);//变为小数位
            tim = 1;//归1
            s--;
            continue;
        }
        else if(str[s]=='-'){//负数
            if(s-1>=0&&str[s-1]!=' '){//防止非法数字,-号应该代表这个数字结束
                return 0.0;
            }
            flag = true;//标记负数
            s--;
            continue;
        }
        else if(str[s]=='+'){//正数
            if(s-1>=0&&str[s-1]!=' '){//防止非法数字,+号应该代表这个数字结束
                return 0.0;
            }
            s--;
            continue;
        }
        ans+=(str[s] - '0')*tim;//否则正常累积
        tim*=10;//位数递增
        s--;
    }
    if(flag)ans = -ans;
    return ans;
}
double Calculate(double a,double b,char c){//运算操作数
      if(c=='+')return a+b;
      else if(c=='-') return a-b;
      else if(c=='*') return a*b;
      else return a/b;
}
int main()
{
    gets(str);
    int len = strlen(str);
    int i = len-1;
    while(i>=0){//从后往前扫
        if(str[i]==' ')i--;//跳过空格
        else if(str[i]>='0'&&str[i]<='9'){//因为从后往前扫的数字部分一定先遇到数字(不论负数还是小数)
            double sum = IsNum(i);//抽出数字
            num.push(sum);//入栈
        }
        else{//否则是运算符
            double a = num.top();//运算规则
            num.pop();
            double b = num.top();
            num.pop();
            double res = Calculate(a,b,str[i]);
            num.push(res);
            i--;
        }
    }
    printf("%.1lf\n",num.top());
    return 0;
}

2.中缀表达式转化前缀表达式

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/82894878