栈的应用 前缀-后缀-中缀表达式的转化

它们都是对表达式的记法,因此也被称为前缀记法、中缀记法和后缀记法。它们之间的区别在于运算符相对与操作数的位置不同:前缀表达式的运算符位于与其相关的操作数之前;中缀和后缀同理。
主要用于计算机处理表达式(栈)

百度百科的科普:

对中缀表达式的计值,并非按运算符出现的自然顺序来执行其中的各个运算,而是根据算符间的优先关系来确定运算的次序,此外,还应顾及括号规则。因此,要从中缀表达式直接产生目标代码一般比较麻烦。

波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示表达式的方法。按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。这种表示法的一个特点是,表达式中各个运算是按运算符出现的顺序进行的,故无须使用括号来指示运算顺序,因而又称为无括号式。
逻辑提问式类似于算术表达式,对于检索而言,这种表达式并不是最优和最简洁的形式,需要进行必要的转换。1929年波兰的逻辑学家卢卡西维兹(Jan Lucasiewicz)提出了将运算符放在运算项后面的逻辑表达式,又称“逆波兰表达式”。采用这种表达式组织逻辑提问式非常方便检索运算,是日本的福岛先生最早将逆波兰表达式应用于情报检索的,故又称为“福岛方法”。
(以上为百度百科内容)

举例:
(3 + 4) × 5 - 6 就是中缀表达式

  • × + 3 4 5 6 前缀表达式
    3 4 + 5 × 6 - 后缀表达式

运算原理:

中缀表达式:操作符以中缀形式处于操作数的中间

前缀表达式/波兰式:

从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果。
例如前缀表达式“- × + 3 4 5 6”:
(1) 从右至左扫描,将6、5、4、3压入堆栈;
(2) 遇到+运算符,因此弹出3和4(3为栈顶元素,4为次顶元素,注意与后缀表达式做比较),计算出3+4的值,得7,再将7入栈;
(3) 接下来是×运算符,因此弹出7和5,计算出7×5=35,将35入栈;
(4) 最后是-运算符,计算出35-6的值,即29,由此得出最终结果。
/ab--->a/b

将中缀表达式转换为前缀表达式:

遵循以下步骤:
(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
(2) 从右至左扫描中缀表达式;
(3) 遇到操作数时,将其压入S2;
(4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
(4-1) 如果S1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈;
(4-2) 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入S1;
(4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
(5) 遇到括号时:
(5-1) 如果是右括号“)”,则直接压入S1;
(5-2) 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃
(6) 重复步骤(2)至(5),直到表达式的最左边;
(7) 将S1中剩余的运算符依次弹出并压入S2;
(8) 依次弹出S2中的元素并输出,结果即为中缀表达式对应的前缀表达式。

前缀变中缀:将每次前缀表达式运算结果加上括号即可

后缀表达式/逆波兰表达式:

与前缀表达式类似,只是顺序是从左至右:
从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 op 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。
例如后缀表达式“3 4 + 5 × 6 -”:
(1) 从左至右扫描,将3和4压入堆栈;
(2) 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素,注意与前缀表达式做比较),计算出3+4的值,得7,再将7入栈;
(3) 将5入栈;
(4) 接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
(5) 将6入栈;
(6) 最后是-运算符,计算出35-6的值,即29,由此得出最终结果。

将中缀表达式转换为后缀表达式:

与转换为前缀表达式相似,遵循以下步骤:
(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
(2) 从左至右扫描中缀表达式;
(3) 遇到操作数时,将其压入S2;
(4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
(4-1) 如果S1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
(4-2) 否则,若优先级比栈顶运算符的高,也将运算符压入S1(注意转换为前缀表达式时是优先级较高或相同,而这里则不包括相同的情况);
(4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
(5) 遇到括号时:
(5-1) 如果是左括号“(”,则直接压入S1;
(5-2) 如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃;
(6) 重复步骤(2)至(5),直到表达式的最右边;
(7) 将S1中剩余的运算符依次弹出并压入S2;
(8) 依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式(转换为 前缀表达式时不用逆序)

下面是笔者写的中缀转化前缀的代码,欢迎指正

#include<iostream>
#include<stack>	
#include<string>
#include<cctype>
using namespace std;
bool Priority(char c,char d){
   int  flagc,flagd;	
   if(c=='*'||c=='/'||c=='%')
       flagc=1;
   if(c=='+'||c=='-')
       flagc=2;
   if(d=='*'||d=='/'||d=='%')
       flagd=1;
   if(d=='+'||d=='-')
       flagd=2;
   return flagc<=flagd;
}
string InfixToPrefix(string Infix){
   string prefix;
   stack<char>ope;
   stack<char>num;
   char c;
   char temp;
   for(int i=Infix.size()-1;i>=0;i--)
   {   c=Infix[i];
       if(isdigit(c)||isalpha(c)){
           num.push(c);
           continue;}
       else{
           if(ope.empty()||c==')'){
               ope.push(c);
               continue;}
           if(c!='('){
               if(Priority(c,ope.top()))
                   ope.push(c);
               else{
                   num.push(ope.top());
                   ope.pop();
                   i++;
                   continue;
               }
               }
           else{
               while(!ope.empty()){
                   if(ope.top()!='('){
                   num.push(ope.top());
                   ope.pop();
                   }else
                       ope.pop();

               }
           }  
       }
       
   }
   while(!ope.empty()){
       num.push(ope.top());
       ope.pop();
   }
   while(!num.empty()){
       prefix+=num.top();
       num.pop();
   }

   return prefix;
}
int main(){
   string s="a*b+c/d-e";
   string s1=InfixToPrefix(s);
   cout<<s1<<endl;
   cout<<"hello"<<endl;
}
posted @ 2021-01-06 22:11  辉与夜  阅读(522)  评论(0)    收藏  举报