前、中、后缀表达式求值与栈

1.首先弄清前缀、中缀、后缀表达式的含义

<1>
中缀表达式即运算符处在两个操作数的中间,该表达式广泛运用于日常,需要人主观进行判断运算符的优先级,不易被机器识别。

<2>
前缀表达式即将运算符写在前面,没有括号的表达式,为纪念波兰籍发明者,也称为波兰式。
虽然将中缀改为前缀之后,表达式的求值可以化简成入栈和出栈两个操作,但较于后缀表达式还是比较复杂。

<3>
后缀表达式即将运算符写在后面,也称为逆波兰式,因为运算顺序就是从左到右运算符出现的顺序,所以操作最简单。

2.中缀表达式的运算逻辑
<1>
分清优先级。
先计算括号内,后计算括号外
在无括号或同层括号类,乘除运算的优先级高于加减运算的优先级
同一优先级运算,从左向右依次进行

<2>
使用两个工作栈,一个存放运算符,一个存放操作数
操作数直接入栈
运算符则与运算栈的栈顶元素比较优先级
⑴若该运算符的优先级更高,则直接入栈
⑵若该运算符的优先级更低,则运算栈的栈顶运算符依次退栈直到左括号或结束⑶若两运算符同级,说明左右括号相遇,左括号此时应该可以直接退栈

<3>
需要进行很多次的条件判断,逻辑有点不清晰,什么时候运算符入栈,什么时候出栈、括号的问题无法很好的处理,不能直接一次解决,又感觉要嵌到许多种情况里判断,最后也无法对整体有一个完整性的把握,无法确定算法处理好了所有可能的情况,无法对结构有清晰的判断。在复杂的逻辑判断的锻炼上还要加强,中缀表达式的直接判断就放弃了,知道转后缀和后缀的计算也行。

3.中缀表达式转后缀表达式
在表达式中存在操作数、小数点、运算符、括号和最后的等号。
需要一个存储后缀表达式的字符数组,和一个存储运算符的字符栈

<1>
表达式实际是存储在字符串中,因此首先要处理操作数的读入。
如果是操作数,直接存入后缀表达式的字符数组中。包括多位数和小数位。

<2>
然后处理括号。
如果是左括号,直接入栈然后处理下一个字符。
如果是右括号,说明左右括号中间的计算完成,将栈顶元素依次退栈存储到字符数组中,直到栈顶元素变为左括号,然后再次退栈,继续下一个字符。

<3>
然后是运算符,首先要确定优先级。可以直接量化,假设
‘=’、‘(‘、’)’的优先级是0
‘+’、‘-’的优先级是1
‘*’、‘/’的优先级是2
当读入的字符是一个运算符时,将它与栈顶元素比较,

如果它的优先级大于栈顶元素,
说明它前面一个操作数要先和它后面一个操作数做该运算,因此直接入栈。

如果它的优先级小于等于栈顶元素,
说明它前面一个数是和这个数前面一个数进行运算的。
所以栈顶元素退栈存入字符数组,然后再将该运算符和新的栈顶元素进行比较。(即一直要退栈顶元素到当前运算的优先级高时入栈)
(在一开始运算栈应该要初始化一个优先级为0的元素,显然如果栈顶元素为该元素,说明当前判断运算符之前的运算已经全部完成,就可以直接入栈)

<4>
最后的’=’直接加上就好,这样一来所有的字符就都能够处理好,并且通过条件判断非常清晰。

//NOJ467题实现代码

#include <iostream>
#include <cstdio>
#include <ctype.h>
using namespace std;

char f(char a,char b)
{
    int aa;int bb;
    if(a=='='||a=='('||a==')') aa=0;
    else if(a=='+'||a=='-')   aa=1;
    else if(a=='*'||a=='/')   aa=2;
    if(b=='='||b=='('||b==')') bb=0;
    else if(b=='+'||b=='-')   bb=1;
    else if(b=='*'||b=='/')   bb=2;

    if(aa>bb)  return '>';
    else if(aa==bb)  return '=';
    else if(aa<bb)   return '<';
}

int main()
{
    int s;cin>>s;
    int t=0,h=0;
    while(s--)
    {

        char S[1000];//栈 
        S[0]='=';int top=0;
        char expresion[2000],str[5000];
        cin>>expresion;
        int i=0;int j=0;
        while(expresion[i]!='\0')
        {
            if(isdigit(expresion[i])||expresion[i]=='.')
            {
                if(expresion[i+1]=='.'||isdigit(expresion[i+1]))
                {
                    str[j++]=expresion[i++];
                }
                else{
                    str[j++]=expresion[i++];
                    str[j++]=' ';
                }
            }
            //处理完操作数和小数点,处理括号
            else if(expresion[i]=='(')
            {
                S[++top]=expresion[i];
                i++;
            }
            else if(expresion[i]==')')
            {
                while(S[top]!='(')
                {
                    str[j++]=S[top];
                    str[j++]=' ';
                    top--;
                }
                top--;i++;
            }
            //处理完括号处理最后的等于 
            else if(expresion[i]=='=')
            {
                while(S[top]!='=')
                {
                    str[j++]=S[top];
                    str[j++]=' ';
                    top--;
                }
                str[j++]='=';str[j]='\0';
                break;
            }
            //最后剩下运算符
            else
            {
                switch(f(expresion[i],S[top]))
                {
                    case '>':S[++top]=expresion[i++];break;
                    case '=':
                    case '<':
                    {
                        while(f(expresion[i],S[top])!='>')
                        {
                            str[j++]=S[top];
                            str[j++]=' ';
                            top--;
                        }
                        S[++top]=expresion[i++];break;
                    }
                }
            } 

        } 
        int t=0;
        while(str[t]!='\0')
        {
           cout<<str[t];
           str[t++]='\0';
        }
        cout<<endl;
    }
}         

4.前、后缀表达式的求值

后缀表达式的计算逻辑非常简单。
如果是操作数,就入栈。
如果是运算符,就将栈顶最上方两个元素出栈进行运算(注意顺序),然后将结果入栈。(退2进1)

而如果是前缀表达式的求值,本来是从右向左扫描,是操作数就入栈,是运算符就取栈顶两元素运算。
但实际上将字符串反序,就和后缀表达式的计算基本一样,(差别主要是顺序和操作数在字符串中的处理)
贴一道计算10以内前缀表达式的代码

//NOJ128题实现代码

#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;

double Stack[510];  //操作数栈 


//字符串s2反序存储到s1 
void strdeal(char *s1,char *s2)
{
    int i=0;
    while(s2[i]!='\0')i++;  //出循环s2[i]=0
    int j=0;i--;
    while(i>=0)
    {
        s1[j]=s2[i];
        j++;i--;
    } 
    s1[j]='\0';
} 

int main()
{
    char str[2000];
    while(gets(str))   //完成输入 
    {
        //反序
        char s[2000];
        strdeal(s,str);   //实现 s为待处理字符串 
        int i=0;
        int top = -1;       //栈顶指针 
        int tag = 0 ;

        while(s[i]!='\0')
        {
            if(isspace(s[i]))   tag=0;   //空格           
            if(tag==1&&s[i]!='.')
            {
                Stack[top]=Stack[top]/10;
                Stack[top]+=s[i]-'0';
            }

            else if(isdigit(s[i])) 
            {
                tag=1;
                Stack[++top]=s[i]-'0';
            }

            else
            {
                switch(s[i])
                {
                    case '+' :
                       Stack[top-1]=Stack[top]+Stack[top-1];
                       Stack[top--]=0;break;
                    case '-' :
                       Stack[top-1]=Stack[top]-Stack[top-1];
                       Stack[top--]=0;break;
                    case '*' :
                       Stack[top-1]=Stack[top]*Stack[top-1];
                       Stack[top--]=0;break;
                    case '/' :
                       Stack[top-1]=Stack[top]/Stack[top-1];
                       Stack[top--]=0;break;
                    default:break;
                }
            } 
            i++;    
        }
        cout<<setprecision(2)<<setiosflags(ios::fixed);
        cout<<Stack[top]<<endl;
    }
}

5.中缀表达式转后缀进行求值
结构中缀转后缀,和后缀表达式的算法,就可以实现中缀表达式的求值了。
贴一道中缀表达式求值的代码

//NOJ35题实现代码

#include <iostream>
#include <ctype.h>
#include <iomanip>
using namespace std;

char f(char a,char b)
{
    int aa;int bb;
    if(a=='='||a=='('||a==')') aa=0;
    else if(a=='+'||a=='-')   aa=1;
    else if(a=='*'||a=='/')   aa=2;
    if(b=='='||b=='('||b==')') bb=0;
    else if(b=='+'||b=='-')   bb=1;
    else if(b=='*'||b=='/')   bb=2;

    if(aa>bb)  return '>';
    else if(aa==bb)  return '=';
    else if(aa<bb)   return '<';
}

int main()
{
    int s;cin>>s;
    while(s--)
    {
        char expresion[1010],S[100],str[2010];
        S[0]='=';int top=0;
        cin>>expresion;int i=0,j=0;
        while(expresion[i]!='\0')
        {
            if(isdigit(expresion[i])||expresion[i]=='.')
            {
                if(expresion[i+1]=='.'||isdigit(expresion[i+1]))
                     str[j++]=expresion[i++];
                else{ str[j++]=expresion[i++];
                      str[j++]=' ';}
            }//处理完操作数和小数点,处理括号
            else if(expresion[i]=='(')
                S[++top]=expresion[i++];
            else if(expresion[i]==')')
            {
                while(S[top]!='(')
                {
                    str[j++]=S[top];
                    str[j++]=' ';
                    top--;
                }
                top--;i++;
            }//处理完括号处理最后的等于 
            else if(expresion[i]=='=')
            {
                while(S[top]!='=')
                {
                    str[j++]=S[top];
                    str[j++]=' ';
                    top--;
                }
                str[j++]='=';str[j]='\0';
                break;
            }//最后剩下运算符
            else
            {
                switch(f(expresion[i],S[top]))
                {
                    case '>':S[++top]=expresion[i++];break;
                    case '=':
                    case '<':{
                                 while(f(expresion[i],S[top])!='>')
                                {
                                    str[j++]=S[top];
                                    str[j++]=' ';
                                    top--;
                                }
                                S[++top]=expresion[i++];break;
                             }
                }
            } 
        }//str存储了后缀表达式,最后以=收尾 

        double Stack[1010];//操作数栈,接下来进行存数
        i=0;top=-1;int tag=0;int t;//小数位t 
        while(str[i]!='\0')
        {
            if(isdigit(str[i]))
            {
                if(tag==2)
                {
                    double temp=str[i]-'0';
                    int tt=t;
                    while(tt--)
                       temp=temp/10; 
                    Stack[top]+=temp;
                    t++;i++;
                }
                else if(tag==1)
                {
                    Stack[top]=Stack[top]*10+str[i]-'0';
                    i++;
                }
                else if(tag==0)
                {
                    Stack[++top]=str[i]-'0';
                    tag=1;i++;
                }
            }
            else if(str[i]=='.')
            {
                tag=2;t=1;i++;
            }
            else if(isspace(str[i]))
            {

                tag=0;i++;
            }
            else
            {
                switch(str[i++])
                {
                    case '+':Stack[top-1]=Stack[top-1]+Stack[top];S[top--]=0;break;
                    case '-':Stack[top-1]=Stack[top-1]-Stack[top];S[top--]=0;break;
                    case '*':Stack[top-1]=Stack[top-1]*Stack[top];S[top--]=0;break;
                    case '/':Stack[top-1]=Stack[top-1]/Stack[top];S[top--]=0;break;
                    case '=':break;
                }
            }
        }
        cout<<setprecision(2)<<setiosflags(ios::fixed);
        cout<<Stack[top]<<endl;

        while(str[i]!='\0')
        str[i++]='\0';

    }
}         

6.过程逻辑与栈的运算
以前处理的问题简单的时候,总是想一想,过程基本就那样几个步骤,一两个条件判断,一两个循环操作,很清晰。
但是当一个问题不是靠着一条路就穿到结尾,而是结构复杂要考虑相互影响的时候,就需要非常清晰有条理的思路了,这大概就是所谓逻辑思维的体现吧,怎么考虑问题的各个层次才可以不漏情况,怎么把问题先抽象成几个步骤然后在分块去实现,这些还要加强。
对于栈,有句在论坛看到的感觉很贴切
“以前我一直以为栈是一种数据结构,但是我做了**题后,才觉得,栈其实是一种思想。”

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值