(附代码!)数据结构实验3-表达式的计算

(附代码!)数据结构实验3-表达式的计算

一、实验目的

熟练掌握栈和队列的存储结构设计及基本操作的实现;学会分析实际问题中具有栈特点的数据结构;了解表达式的前缀、中缀、后缀等计算机内表示形式。

二、实验内容与要求

按常规形式(即中缀表达式)输入算术表达式(例如:输入2*(6-4)+8/4),要求能够:
⑴生成表达式的前缀、后缀表示,并输出;
⑵基于表达式的前缀、后缀、中缀表示,对该表达式求值;
⑶编写一个主程序对表达式求值函数进行测试。

三、算法思路
1、中缀表达式计算:分别设计运算符栈和运算数栈,其分别对应着初始化、出入栈和得到栈顶元素的操作。
对于整个表达式遍历:遇到数字入运算数栈、遇到运算符与运算符栈顶的符号比较优先级:①栈顶元素优先级低:入运算符栈,表达式指针后移一位②相等时脱括号并接收下一字符③大于时运算数栈出两个数并将计算结果入运算数栈。
2、后缀表达式计算:将转换的后缀表达式作为参数计算数值。从左到右遍历后缀表达式,遇到数字就入栈,遇到运算符就将栈顶的两个元素出栈计算结果再入栈。直至表达式遍历完毕。
3、前缀表达式计算:将后缀表达式全部倒置形成新的表达式,用后缀计算的方法相同计算即可。
4、中缀表达式转后缀表达式:遇到操作数直接输出,遇到操作符同1中的处理压栈、入栈。最后输出运算符栈内剩余的全部运算符即可。
中缀表达式转前缀表达式:先转化为后缀表达式,再用后缀表达式转化为前缀表达式。先将后缀表达式全部倒置形成的新表达式按照转后缀的过程转换,转化之后再将该表达式从尾到头输出即为对应的前缀表达式。

四、上代码!


#include <iostream>
using namespace std;
#define OPSETSIZE 7   //运算符个数共7个
#define STACK_INIT_SIZE 100   //存储空间初始分配量
#define STACKINCREMENT 10    //存储空间分配增量

//运算符集合与下面二维数组行和列的顺序相同
char OPSET[OPSETSIZE]={'+' , '-' , '*' , '/' ,'(' , ')' , '#'};

unsigned char Prior[7][7] = {     // 表3.1  算符间的优先关系
      '>','>','<','<','<','>','>',
      '>','>','<','<','<','>','>',
      '>','>','>','>','<','>','>',
      '>','>','>','>','<','>','>',
      '<','<','<','<','<','=',' ',
      '>','>','>','>',' ','>','>',
      '<','<','<','<','<',' ','='
};
unsigned char Prior_2[7][7] = {     // 转前缀时用
      '>','<','<','<','<','>','>',
      '<','>','<','<','<','>','>',
      '>','>','>','<','<','>','>',
      '>','>','<','>','<','>','>',
      '<','<','<','<','<','=',' ',
      '>','>','>','>',' ','>','>',
      '<','<','<','<','<',' ','='
};



typedef struct    // 运算数栈,实数元素
{
    float *base;
    float *top;
    int stacksize;
}StackFloat;



typedef struct    // 运算符栈,字符元素
{
    char *base;
    char *top;
    int stacksize;
}StackChar;



void InitStack(StackChar &S)      //初始化运算符栈
{
    //构造一个空栈
    S.base = (char *)malloc(STACK_INIT_SIZE * sizeof(char));
    if(!S.base)
        exit(0);
    S.top = S.base;
    S.stacksize = STACK_INIT_SIZE;
}


void InitStack(StackFloat &S)     //初始化运算数栈
{
    //构造一个空栈
    S.base = (float *)malloc(STACK_INIT_SIZE * sizeof(float));
    if(!S.base)
        exit(0);
    S.top = S.base;
    S.stacksize = STACK_INIT_SIZE;
}


void Push(StackChar &S,char e)   //插入元素e为新的栈顶元素
{
    if(S.top - S.base >= S.stacksize)   //栈满,追加存储空间
    {
        S.base = (char *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(char));
        if(!S.base)
            exit(0);
    }
    *S.top++ = e;
}



void Push(StackFloat &S,float e)   //插入元素e为新的栈顶元素
{
    if(S.top - S.base >= S.stacksize)   //栈满,追加存储空间
    {
        S.base = (float *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(float));
        if(!S.base)
            exit(0);
    }
    *S.top++ = e;
}


char GetTop(StackChar S)     //得到运算符
{
    return *(S.top-1);
}


float GetTop(StackFloat S)     //得到运算数
{
    return *(S.top-1);
}



void Pop(StackChar &S,char &e) //若栈不空,则删除S的栈顶元素,用e返回其值
{
    e = *--S.top;
}


void Pop(StackFloat &S,float &e) //若栈不空,则删除S的栈顶元素,用e返回其值
{
    e = *--S.top;
}


float Operate(float a,unsigned char theta, float b)   //计算值 a+-*/b的
{
   switch(theta)
   {
      case '+': return a+b;
      case '-': return a-b;
      case '*': return a*b;
      case '/': return a/b;
      default : return 0;
   }
}


bool In(char Test,char* TestOp)   //找到该运算符返回true TestOp就是OPSET运算符数组
{
   for (int i=0; i< OPSETSIZE; i++)  //i<运算符个数7
    {
      if (Test == TestOp[i])
          return true;
   }
   return false;
}


int ReturnOpOrd(char op,char* TestOp)     //在OPSET运算符数组中返回该运算符下标
{
   int i;
   for(i=0; i< OPSETSIZE; i++)
    {
        if (op == TestOp[i])
            return i;
    }
   return 0;
}


char precede(char Aop, char Bop,unsigned char P[7][7])     //比较栈顶符号和正在比较的符号优先级
{
    int m,n;
    m = ReturnOpOrd(Aop,OPSET);
    n = ReturnOpOrd(Bop,OPSET);
    //return Prior[m][n];
    return P[m][n];
}


//将字符串倒置 在转前缀时用到 str1是原来的 str2是转置后的
void AdverseStr(char *str1,char *str2,int length)
{
    int k = 0;
    for(int i = length-1; i >= 0; i--)
    {
        if(str1[i] == '(')
            str2[k++] = ')';
        else if(str1[i] == ')')
            str2[k++] = '(';
        else
            str2[k++] = str1[i];
    }
    str2[k] = '\0';
}



float EvaluateExpression(char* MyExpression)
{
   // 算术表达式求值的算符优先算法。
   // 设OPTR和OPND分别为运算符栈和运算数栈,OP为运算符集合。
   StackChar  OPTR;    // 运算符栈,字符元素
   StackFloat OPND;    // 运算数栈,实数元素
   char TempData[20];
   float Data,a,b;
   char theta,*c,x,Dr[2];    //*c指向表达式
   
   InitStack (OPTR);    //初始化运算符栈
   Push (OPTR, '#');
   InitStack (OPND);    //初始化运算数栈
   c = MyExpression;
   strcpy(TempData,"\0");
    //可以让TempData变为字符串 因为在下面的atof中用的是字符串
   while (*c!= '#' || GetTop(OPTR)!= '#')     //未读到#时或未到栈底时
   {
      if (!In(*c, OPSET))     //不是运算符 是数字
      {
          Dr[0]=*c;
          Dr[1]='\0';
          strcat(TempData,Dr);   //把数字存在TempData数组里 其实里面只有一个数字
          c++;
         if(In(*c,OPSET))     //判断数字的下一个是字符->将数字入数据OPND栈 条件为真
         {
             //将char转化为float型的data double atof(const char *str)
            Data=(float)atof(TempData);
            Push(OPND, Data);    //让数据data入栈
            strcpy(TempData,"\0");   //将TempData重新置为空
         }
      }
      else
      {   // 不是运算符则进栈
         switch (precede(GetTop(OPTR), *c,Prior))
         {
            case '<':   // 栈顶元素优先权低
                 Push(OPTR, *c);
                 c++;
                 break;
            case '=':   // 脱括号并接收下一字符
                 Pop(OPTR, x);
                 c++;
                 break;
            case '>':   // 退栈并将运算结果入栈
                 Pop(OPTR, theta);    //a theta b (数 符号 数)
                 Pop(OPND, b);
                 Pop(OPND, a);
                 Push(OPND, Operate(a, theta, b));
                 break;
         } // switch
      }
   } // while
   return GetTop(OPND);   //返回运算数字栈顶
}



float EvaluateBack(char* MyExpression)
{
    char *c = MyExpression;
    strcat(c, "#");
    char TempData[20],Dr[2],theta;   //theta是运算符
    float Data,a,b;
    strcpy(TempData,"\0");
    
    StackFloat OPND;    // 运算数栈,实数元素
    InitStack(OPND);
    while (*c!= '#')     //未读到#时或未到栈底时
    {
        if (!In(*c, OPSET))     //不是运算符 是数字->Push
        {
            Dr[0]=*c;
            Dr[1]='\0';
            strcat(TempData,Dr);   //把数字存在TempData数组里 其实里面只有一个数字
            c++;
            
            //将char转化为float型的data double atof(const char *str)
            Data=(float)atof(TempData);
            Push(OPND, Data);    //让数据data入栈
            strcpy(TempData,"\0");   //将TempData重新置为空
        }
        else
        {
            theta = *c;
            Pop(OPND, b);
            Pop(OPND, a);
            Push(OPND, Operate(a, theta, b));
            c++;
        }
    }
    return GetTop(OPND);   //返回运算数字栈顶
}


float EvaluateFront(char* MyExpression)
{
    char *exp1 = MyExpression;     //exp1是原始的前缀表达式
    char *exp2;      //exp2是转置后的表达式
    int length = 0;
    for(int i = 0; exp1[i] != '\0';i++)
        length++;
    char F_adverse[length];
    exp2 = F_adverse;
    AdverseStr(exp1, exp2, length);
    
    
    char *c = exp2;
    strcat(c, "#");
    char TempData[20],Dr[2],theta;   //theta是运算符
    float Data,a,b;
    strcpy(TempData,"\0");
    
    StackFloat OPND;    // 运算数栈,实数元素
    InitStack(OPND);
    while (*c!= '#')     //未读到#时或未到栈底时
    {
        if (!In(*c, OPSET))     //不是运算符 是数字->Push
        {
            Dr[0]=*c;
            Dr[1]='\0';
            strcat(TempData,Dr);   //把数字存在TempData数组里 其实里面只有一个数字
            c++;
            
            //将char转化为float型的data double atof(const char *str)
            Data=(float)atof(TempData);
            Push(OPND, Data);    //让数据data入栈
            //cout<<Data;
            strcpy(TempData,"\0");   //将TempData重新置为空
        }
        else
        {
            theta = *c;
            Pop(OPND, a);
            Pop(OPND, b);
            Push(OPND, Operate(a, theta, b));
            //cout<<Operate(a, theta, b);
            c++;
        }
    }
    return GetTop(OPND);   //返回运算数字栈顶
}

char * MidToBack(char *exp1,char *exp2,unsigned char P[7][7])     //中缀转后缀
{
    int k = 0;
    char x,theta;    //保存运算符符号的
    char *c = exp1;
    StackChar  OPTR;    // 运算符栈,字符元素
    InitStack (OPTR);    //初始化运算符栈
    Push (OPTR, '#');
    
    while (*c!= '#' || GetTop(OPTR)!= '#')     //未读到#时或未到栈底时
    {
        if(*c > '0'&& *c < '9')   //遇到数字直接输出
        {
            exp2[k++] = *c;
            c++;
        }
        else     //遇到运算符
        {
            //switch (precede(GetTop(OPTR), *c))
            switch (precede(GetTop(OPTR), *c,P))
            {
               case '<':   // 栈顶元素优先权低
                    Push(OPTR, *c);
                    c++;
                    break;
               case '=':   // 脱括号并接收下一字符
                    Pop(OPTR, x);
                    c++;
                    break;
               case '>':   // 退栈并将运算结果入栈
                    Pop(OPTR, theta);
                    exp2[k++] = theta;
                    break;
            } // switch
        }
    }
    return exp2;
}



char *MidToFront(char *exp1,char *exp2)
{
    int length = 0,len = 0;
    for(int i = 0; exp1[i] != '#';i++)
        length++;
    char temp[length],final[length];
    char *exp_temp = temp;
    char *exp_final = final;
    AdverseStr(exp1, exp_temp, length);
    
    
    strcat(exp_temp, "#");
    MidToBack(exp_temp, exp_final,Prior_2);     //转换完结果是exp_final
    for(int i = 0; exp_final[i] != '\0'; i++)
        len++;
    AdverseStr(exp_final, exp2, len);
    if(exp2[0] == ')')
    {
        for(int i = 0; i < len; i++)
            exp2[i] = exp2[i+1];
    }
    return exp2;
}





int main()
{
    char Mid[20],Back[20],Front[20];
    char *mid, *back, *front;
    mid = Mid;
    back = Back;
    front = Front;
    cout<<"请输入要计算的中缀表达式(#表示输入结束):"<<endl;
    cin>>mid;
    cout<<"中缀计算结果为:"<<EvaluateExpression(mid)<<endl;
    cout<<"后缀表达式为:"<<MidToBack(mid, back,Prior)<<endl;
    cout<<"后缀计算结果为:"<<EvaluateBack(back)<<endl;
    cout<<"前缀表达式为:"<<MidToFront(mid, front)<<endl;
    cout<<"前缀计算结果为:"<<EvaluateFront(front)<<endl;
    cout<<endl;
}


五、运行结果
在这里插入图片描述

六、心得体会
在做前缀表达式时遇到了许多问题,首先是如何将整个字符串倒置,我选用的是遍历一次字符串得到长度,再用for循环从后向前输出新的字符串。第二个问题是前缀计算和转换时要注意它的几个特殊性。前缀计算弹栈时应该将栈顶元素作为第一个计算数,倒数第二个元素作为第二个,这与后缀计算相反。转前缀表达式时同级运算符的优先级也会发生变化。

👧帮助到你请点赞➕收藏➕关注哦~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值