C++堆栈应用(二):后缀表达式求值

在日常生活中,我们接触到的算术表达式多为中缀表达式,其一般形式为:
5+6/2-3*4
即运算符位于两个参与运算的运算数的中间。
而在计算机对算术表达式求值时,却多是基于后缀表达式来进行求值。前面的中缀表达式转化为后缀表达式后,其形式为:
5 6 2 / + 3 4 * -
即运算符紧跟参与运算的两个运算数之后。

本文是关于如何在CodeBlocks编辑器中,用C++语言实现后缀表达式求值,所参考的代码来源于浙大陈越、何钦铭所编的《数据结构(第2版)》(高等教育出版社,2016),P71-81.
下文是对关键代码的阐释。

1.GetOp()函数:读入运算符或运算数
从后缀表达式中读入一个对象(运算数或运算符),并将其保存、返回。
GetOp()函数的三个形参参数:
1.char *Expr 是指向后缀表达式(一个字符数组)的字符指针;
2.int *index 是指示后缀表达式(字符数组)下标的整型指针;
3.char *str 是指向保存所读对象(运算数或运算符)的字符指针。

Type GetOp(char *Expr,int *index,char *str)
{
    int i=0;

    while((str[i]=Expr[(*index)++])==' '); //跳过后缀表达式前的空格
    while(str[i]!=' ' && str[i]!='\0'){
        str[++i]=Expr[(*index)++];  //将后缀表达式中的运算数或运算符存入str[]数组中
    }
    if(str[i]=='\0')
        (*index)--;  //如果读到后缀表达式的结尾,使下标停在结束符处
    str[i]='\0';   //写入结束标志

    if(i==0)                 //判断是否读到后缀表达式的结束
        return end;
    else if(isdigit(str[0])) //判断str[]中存入的是否为运算数
        return num;
    else                    //判断str中是否存的运算符
        return opr;
}

GetOp()函数的返回值为一个枚举类型的对象,该类型的定义为:

enum Type{num,opr,end};

类型名为Type,其中num,opr,end这三个枚举常量分别对应运算符、运算数、字符串结尾

2.PostfixExp()函数:调用GetOp()函数,读入后缀表达式并求值

ElementType PostfixExpr(char *Expr)
{
    Stack sta,*S;
    S=&sta;
    Type T;
    ElementType Op1,Op2;
    char str[MaxSize];
    int index=0;

    InitStack(S);
    Op1=Op2=0;
    /*当未读到结束标识符时*/
    while((T=GetOp(Expr,&index,str))!=end){
        if(T==num)  //如果读到运算数,存入栈顶
            Push(S,atof(str));

        else{      //如果读到运算符,从栈中弹出最上面两个元素进行运算
            if(S->top!=-1)
                Op2=Pop(S);
            else
                Op2=INFINITY; //标记异常
            if(S->top!=-1)
                Op1=Pop(S);
            else
                Op2=INFINITY;
            switch(str[0]){
                case '+':Push(S,Op1+Op2);break;
                case '-':Push(S,Op1-Op2);break;
                case '*':Push(S,Op1*Op2);break;
                case '/':{
                    if(Op2!=0.0)
                        Push(S,Op1/Op2);
                    else{
                        cout<<"错误:分母除法为0"<<endl;
                    }
                    break;
                }
                default:cout<<"未知运算符"<<str[0]<<endl;Op2=INFINITY;break;
            }
            if(Op2>=INFINITY)
                break;    //若标记异常,退出while()循环
        }

    }
    if(Op2<INFINITY)  //若读数过程无异常
        if(S->top!=-1)
            Op2=Pop(S);
        else
            Op2=INFINITY;
    delete S;
    return(Op2);
}

这个函数有几处需要说明:
(1)Op2=INFINITY
Op1和Op2分别表示参与一次四则远算的两个运算数,例如在“4/2”的除法运算中,Op1=4,Op2=2。作者用Op2=INFINITY语句来标志在读入后缀表达式的过程中遇到异常(例如运算符数目异常、运算符为±*/之外的其他符号、被除数为0等)。INFINITY代表正无穷:

#define INFINITY 1e9 

(2)isdigit()函数
该函数属于ctype.h标准库,若要调用该函数,需要在头文件中写入:

#include <ctype.h>

该函数的作用是判断一个字符是否是十进制数字(‘0’~‘9’),若是,返回1,否则,返回0.例如:

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

int main()
{
    char str1,str2;
    cin>>str1>>str2;
    cout<<isdigit(str1)<<endl;
    cout<<isdigit(str2)<<endl;
    return 0;
}

运行结果为:
在这里插入图片描述
(3)atof()函数
该函数属于stdlib.h标准库,若要调用该函数,需要在头文件中写入:

#include <stdlib.h>

其作用是将一个由十进制数字和小数点组成的字符串转换成浮点数,例如:

#include <iostream>
#include <cstring>
#include <stdlib.h>
using namespace std;

int main()
{
    char str[10];
    strcpy(str,"2.1415");
    cout<<1.0+atof(str)<<endl;
    return 0;
}

运行程序会输出3.1415

以下是用顺序栈实现的完整代码:

#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <ctype.h>
#define MaxSize 20    //操作数序列可能的最大长度
#define INFINITY 1e9  //代表正无穷
using namespace std;
typedef double ElementType;
/*定义枚举类型,其中的三个元素分别对应运算数、运算符、字符串结尾*/
enum Type{num,opr,end};
/*定义顺序栈结构类型*/
struct Stack
{
    ElementType data[MaxSize];
    int top;
};

int main()
{
    ElementType PostfixExpr(char *Expr);

    char Ex[MaxSize];
    cin.getline(Ex,MaxSize);  //读入带空格的字符串
    ElementType result;
    char *Expr=Ex;

    result=PostfixExpr(Expr);
    if(result<INFINITY)
        cout<<result<<endl;
    else
        cout<<"表达式错误"<<endl;

    return 0;
}

/*初始化顺序栈*/
void InitStack(Stack *s)
{
    s->top=-1;
}

/*入栈:将元素x读入栈顶元素*/
void Push(Stack *s,ElementType x)
{
    if(s->top==MaxSize-1)
        cout<<"栈已满";
    else{
        s->data[++(s->top)]=x;
    }
}

/*退栈:弹出栈顶元素*/
ElementType Pop(Stack *s)
{
    if(s->top==-1){
        cout<<"栈空";
        return 0;
    }
    else{
        return(s->data[(s->top)--]);
    }
}

/*从后缀表达式中读入一个对象(运算数或运算符),并将其保存、返回。
GetOp()函数的三个形参参数:
1.char *Expr 是指向后缀表达式(一个字符数组)的字符指针;
2.int *index 是指示后缀表达式(字符数组)下标的整型指针;
3.char *str 是指向保存所读对象(运算数或运算符)的字符指针。
函数的返回值是一个枚举类型
*/
Type GetOp(char *Expr,int *index,char *str)
{
    int i=0;

    while((str[i]=Expr[(*index)++])==' '); //跳过后缀表达式前的空格
    while(str[i]!=' ' && str[i]!='\0'){
        str[++i]=Expr[(*index)++];  //将后缀表达式中的运算数或运算符存入str[]数组中
    }
    if(str[i]=='\0')
        (*index)--;  //如果读到后缀表达式的结尾,使下标停在结束符处
    str[i]='\0';   //写入结束标志

    if(i==0)                 //判断是否读到后缀表达式的结束
        return end;
    else if(isdigit(str[0])) //判断str[]中存入的是否为运算数
        return num;
    else                    //判断str中是否存的运算符
        return opr;
}

/*调用GetOp()函数读入的后缀表达式,并求值*/
ElementType PostfixExpr(char *Expr)
{
    Stack sta,*S;
    S=&sta;
    Type T;
    ElementType Op1,Op2;
    char str[MaxSize];
    int index=0;

    InitStack(S);
    Op1=Op2=0;
    /*当未读到结束标识符时*/
    while((T=GetOp(Expr,&index,str))!=end){
        if(T==num)  //如果读到运算数,存入栈顶
            Push(S,atof(str));

        else{      //如果读到运算符,从栈中弹出最上面两个元素进行运算
            if(S->top!=-1)
                Op2=Pop(S);
            else
                Op2=INFINITY; //标记异常
            if(S->top!=-1)
                Op1=Pop(S);
            else
                Op2=INFINITY;
            switch(str[0]){
                case '+':Push(S,Op1+Op2);break;
                case '-':Push(S,Op1-Op2);break;
                case '*':Push(S,Op1*Op2);break;
                case '/':{
                    if(Op2!=0.0)
                        Push(S,Op1/Op2);
                    else{
                        cout<<"错误:分母除法为0"<<endl;
                    }
                    break;
                }
                default:cout<<"未知运算符"<<str[0]<<endl;Op2=INFINITY;break;
            }
            if(Op2>=INFINITY)
                break;    //若标记异常,退出while()循环
        }

    }
    if(Op2<INFINITY)  //若读数过程无异常
        if(S->top!=-1)
            Op2=Pop(S);
        else
            Op2=INFINITY;
    delete S;
    return(Op2);
}

以下是用链栈实现的完整代码:

#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <ctype.h>
#define MaxSize 20    //操作数序列可能的最大长度
#define INFINITY 1e9  //代表正无穷
using namespace std;
typedef double ElementType;
/*定义枚举类型,其中的三个元素分别对应运算数、运算符、字符串结尾*/
enum Type{num,opr,end};
/*定义链栈结构类型*/
typedef struct SNode
{
    ElementType data;
    SNode *next;
}Stack;

int main()
{
    ElementType PostfixExpr(char *Expr);

    char Ex[MaxSize];
    cin.getline(Ex,MaxSize);  //读入带空格的字符串
    ElementType result;
    char *Expr=Ex;

    result=PostfixExpr(Expr);
    if(result<INFINITY)
        cout<<result<<endl;
    else
        cout<<"表达式错误"<<endl;

    return 0;
}

/*初始化链栈*/
void InitStack(Stack *s)
{
    s->next=NULL;
}

/*入栈:将元素x读入栈顶元素*/
void Push(Stack *s,ElementType x)
{
    Stack *p;
    p=new Stack;
    p->data=x;
    p->next=s->next;
    s->next=p;
}

/*退栈:弹出栈顶元素*/
ElementType Pop(Stack *s)
{
    Stack *p;
    ElementType x;
    if(s->next==NULL){
        cout<<"栈空";
        return 0;
    }
    else{
        p=s->next;
        x=p->data;
        s->next=p->next;
        delete p;
        return(x);
    }
}

/*从后缀表达式中读入一个对象(运算数或运算符),并将其保存、返回。
GetOp()函数的三个形参参数:
1.char *Expr 是指向后缀表达式(一个字符数组)的字符指针;
2.int *index 是指示后缀表达式(字符数组)下标的整型指针;
3.char *str 是指向保存所读对象(运算数或运算符)的字符指针。
函数的返回值是一个枚举类型
*/
Type GetOp(char *Expr,int *index,char *str)
{
    int i=0;

    while((str[i]=Expr[(*index)++])==' '); //跳过后缀表达式前的空格
    while(str[i]!=' ' && str[i]!='\0'){
        str[++i]=Expr[(*index)++];  //将后缀表达式中的运算数或运算符存入str[]数组中
    }
    if(str[i]=='\0')
        (*index)--;  //如果读到后缀表达式的结尾,使下标停在结束符处
    str[i]='\0';   //写入结束标志

    if(i==0)                 //判断是否读到后缀表达式的结束
        return end;
    else if(isdigit(str[0])) //判断str[]中存入的是否为运算数
        return num;
    else                    //判断str中是否存的运算符
        return opr;
}

/*调用GetOp()函数读入的后缀表达式,并求值*/
ElementType PostfixExpr(char *Expr)
{
    Stack sta,*S;
    S=&sta;
    Type T;
    ElementType Op1,Op2;
    char str[MaxSize];
    int index=0;

    InitStack(S);
    Op1=Op2=0;
    /*当未读到结束标识符时*/
    while((T=GetOp(Expr,&index,str))!=end){
        if(T==num)  //如果读到运算数,存入栈顶
            Push(S,atof(str));

        else{      //如果读到运算符,从栈中弹出最上面两个元素进行运算
            if(S->next!=NULL)
                Op2=Pop(S);
            else
                Op2=INFINITY; //标记异常
            if(S->next!=NULL)
                Op1=Pop(S);
            else
                Op2=INFINITY;
            switch(str[0]){
                case '+':Push(S,Op1+Op2);break;
                case '-':Push(S,Op1-Op2);break;
                case '*':Push(S,Op1*Op2);break;
                case '/':{
                    if(Op2!=0.0)
                        Push(S,Op1/Op2);
                    else{
                        cout<<"错误:分母除法为0"<<endl;
                    }
                    break;
                }
                default:cout<<"未知运算符"<<str[0]<<endl;Op2=INFINITY;break;
            }
            if(Op2>=INFINITY)
                break;    //若标记异常,退出while()循环
        }

    }
    if(Op2<INFINITY)  //若读数过程无异常
        if(S->next!=NULL)
            Op2=Pop(S);
        else
            Op2=INFINITY;
    delete S;
    return(Op2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值