【练习】哈工大数据结构实验——算术表达式求值

一、实验目的

  1. 通过本实验,理解栈这种基本数据结构,并掌握编程实现栈的一些基本操作函数;
  2. 理解前缀、中缀、后缀表达式的定义,学习前缀、中缀、后缀表达式的计算;
  3. 用栈的“先进后出”性质进行中缀表达式转后缀表达式和求值;

二、实验要求及实验环境

1、实验要求:

  1. 从键盘任意输入一个语法正确的中缀表达式, 保存该表达式;
  2. 利用栈结构,将上述中缀表达式转换成后缀表达式,并显示栈的变化过程和所得到的后缀达式。
  3. 利用栈结构,对上式后缀表达式进行求值,并显示栈的状态变化过程和最终结果。
  4. 表达式中的字符有:运算符‘+’、‘-’、‘*’、‘/’、‘(’、‘)’;实数;“#”(区分标记负号和减号);

2、实验环境:

Code:blocks


三、设计思想

1、逻辑设计

1.中缀表达式转后缀表达式的算法:
设立一个栈,存放运算符,首先栈为空,程序从左到右扫描中缀表达

  • 若遇到操作数,存放到后缀表达式的数组中,并输出一个空格作为两个操作数的分隔符(这里注意多位数处理,多位数处理数字与数字之间不要空格);
  • 若遇到运算符,则必须与栈顶比较,运算符级别比栈顶级别高则进否则退出栈顶元素并存放在后缀表达式中,然后输出一个空格作分隔符;
  • 若遇到左括号,进栈;
  • 若遇到右括号,则一直退栈输出,直到退到左括号止。当栈变成空时,输出的结果即为后缀表达式。

2.计算后缀表达式算法:
设置一个栈,开始时,栈为空,然后从左到右扫描后缀表达式:

  • 若遇操作数,字符型转化为double类型,进栈;
  • 若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕。
  • 此时,栈中仅有一个元素,即为运算的结果。

2、物理设计

  流程图(最左边为主函数,右边为3个子函数及其调用关系):

这里写图片描述


四、测试结果

前缀转后缀:

这里写图片描述

后缀表达式计算:

这里写图片描述


注:-100+((-2.5+3.1*2)/5.1-3.2)/2.8样例包括了程序所处理的几种边界情况:

  • 第一个数为负数处理;
  • 括号后面的第一个数为负数处理;
  • 多位数运算处理;
  • 浮点数计算处理;

五、系统不足与经验体会

1、系统不足

  1. 定义了两个栈,但是实际上可以用类模版减少代码量;
  2. 不能实现取余、求阶乘等其它一些运算,只能实现加减乘除的四则运算;
  3. 对于输入非法的中缀表达式没有进行提示和处理;

2、经验体会

  1. 在处理多位数的时候用空格间隔来标记不同的数,这样实现起来比较方便;
  2. 在处理负号的时候,曾尝试直接把它压栈当作普通的运算符处理,但会导致后面实现起来比较混乱,后来用特殊字符‘#’来标记负号就容易实现;
  3. 在进行下一次操作之前,要把上一次的数组清空,这在调试的时候很隐蔽;
  4. 栈真的很有用,用得好和巧妙能把某些问题解决得很漂亮。

六、源码(C++)

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define  maxlen 100
using namespace std;
struct STACK_CHAR
{
    int top_char;
    char element[maxlen];
} op_s;
struct STACK_NUM
{
    int top_num;
    double element[maxlen];
} num_s;
char mid[100],end[100];//存放中缀和后缀
void makenull_char(STACK_CHAR &S)
{
    S.top_char=maxlen;
}
void makenull_num(STACK_NUM &S)
{
    S.top_num=maxlen;
}
bool empty_char(STACK_CHAR S)
{
    if (S.top_char>maxlen-1)   return true;
    else return false ;
}
bool empty_num(STACK_NUM S)
{
    if (S.top_num>maxlen-1)   return true;
    else return false ;
}
char top_char(STACK_CHAR S)
{
    if (empty_char(S)) return NULL;
    else return(S.element[S.top_char]);
}
double top_num(STACK_NUM S)
{
    if (empty_num(S)) return NULL;
    else return(S.element[S.top_num]);
}
void pop_char(STACK_CHAR &S)
{
    if (empty_char(S))  cout << "STACK_CHAR is empty" << endl;
    else
    {
        S.top_char =S.top_char+1;
        cout << "符号栈中元素:" ;
        for(int i=maxlen-1; i>=S.top_char; i--)  cout << S.element[i] << " ";
        cout << endl;
    }
}
void pop_num(STACK_NUM &S)
{
    if (empty_num(S))  cout << "STACK_NUM is empty" << endl;
    else
    {
        S.top_num =S.top_num+1;
        cout << "数字栈中元素:";
        for(int i=maxlen-1; i>=S.top_num; i--)   cout << S.element[i] << " ";
        cout << endl;
    }
}
void push(char x,STACK_CHAR &S)
{
    if (S.top_char==0)   cout << " STACK_CHAR  is full " << endl;
    else
    {
        S.top_char=S.top_char-1;
        S.element[S.top_char]=x;
        cout << "符号栈中元素:" ;
        for(int i=maxlen-1; i>=S.top_char; i--)  cout << S.element[i] <<  " ";
        cout << endl;
    }
}
void push_num(double x,STACK_NUM &S)
{
    if (S.top_num==0)   cout << " STACK_NUM is full " << endl;
    else
    {
        S.top_num=S.top_num-1;
        S.element[S.top_num]=x;
        cout << "数字栈中元素:";
        for(int i=maxlen-1; i>=S.top_num; i--)    cout << S.element[i] << " ";
        cout << endl;
    }
}
/*栈的一系列基本操作函数*/
int pre_judge(char x);//优先级判断
void mid_to_end(char a[],int n);//转化的函数
double calculate_end(char end[],int n);//计算后缀表达式函数
int main()
{
    int j;
    makenull_char(op_s);
    cout << "请输入前缀表达式:";
    gets(mid);
    cout << "前缀表达式为:";
    puts(mid);
    mid_to_end(mid,100);
    cout << endl << "后缀表达式为:" ;
    for (j=0; end[j]!='\0'; j++)
    {
        if(end[j]=='#') cout << '-';//扫描遇到井号那么显示负号
        else  cout << end[j];    //显示后缀表达式
    }
    cout << endl << endl;
    cout <<"表达式的计算结果为:"<< calculate_end(end,100) << endl;
    return 0;
}
int pre_judge(char x)//符号优先级判断
{
    int flag=0;
    switch(x)
    {
    case '(':
        flag=1;
        break;
    case ')':
        flag=1;
        break;
    case '+':
        flag=2;
        break;
    case '-':
        flag=2;
        break;
    case '*':
        flag=3;
        break;
    case '/':
        flag=3;
        break;
    default :
        break;
    }
    return flag;
}
void mid_to_end(char a[],int n)//中缀转后缀函数
{
    int end_count=0,i,m=0;
    if(a[0]=='-')//中缀表达式中是第一个为负号
    {
        end[end_count]='#';//用#号标记负号
        end_count++;
        m=1;//如果为负号,进入循环扫描的时候就从第二个字符开始
    }
    for (i=m; a[i]!='\0'; i++)
    {
        if (a[i]<='9'&&a[i]>='0'&&(a[i+1]>'9'||a[i+1]<'0')&&a[i+1]!='.')
        {
            end[end_count]=a[i];
            end_count++;
            end[end_count]=' ';
            end_count++;
        }//当前字符为0~9而且下一个为运算符(即为数字的最后一位,那么设空格与下一个数字分开)
        else if((a[i]<='9'&&a[i]>='0'&&((a[i+1]<='9'&&a[i+1]>='0')||a[i+1]=='.'))||a[i]=='.')
        {
            end[end_count]=a[i];
            end_count++;
        }//是数字而且不是数字的最后一位,后最中不设空格
        else if(a[i-1]=='('&&a[i]=='-')
        {
            end[end_count]='#';
            end_count++;
        }//中缀表达式扫描到的数字的前一个是左括号则当负号处理
        else if((a[i]=='+'||a[i]=='-'||a[i]=='*'||a[i]=='/')&&a[i-1]!='(')
        {
            while(!empty_char(op_s))
            {
                if(pre_judge(top_char(op_s))>=pre_judge(a[i]))
                {
                    end[end_count]=top_char(op_s);
                    pop_char(op_s);
                    end_count++;
                    end[end_count]=' ';
                    end_count ++;
                }
                else break;
            }
            push(a[i],op_s);
        }/*若遇到运算符,则必须与栈顶比较,运算符级别比栈顶级别高则进栈,
        否则退出栈顶元素并存放在后缀表达式中,然后输出一个空格作分隔符;*/
        else if (a[i]=='(') push(a[i],op_s);//若遇到左括号,进栈;
        else  if(a[i]==')')
        {
            while(1)
            {
                if(top_char(op_s)=='(')
                {
                    pop_char(op_s);
                    break;
                }
                else
                {
                    end[end_count]=top_char(op_s);
                    pop_char(op_s);
                    end_count++;
                    end[end_count]=' ';//注意设置空格
                    end_count++;
                }
            }
        }//若遇到右括号,则一直退栈输出,直到退到左括号止。
        else  continue;
    }
    while(!empty_char(op_s))//将栈中剩下的元素弹出
    {
        end[end_count]=top_char(op_s);
        pop_char(op_s);
        end_count++;
        end[end_count]=' ';
        end_count++;
    }
    end[end_count]='\0';//设置字符串结束标识
}
double calculate_end(char a[],int n)        //计算后缀表达式的值
{
    double top,sec,ans=0,number;
    int i=0,j=0,k,x;
    bool flag=true;//flag用于标记是否为负数,开始时设定为不是负数
    char num[100];
    STACK_NUM num_s;
    makenull_num(num_s);
    for(i=0; end[i]!='\0'; i++)
    {
        if(end[i]=='#')
        {
            flag=false;
            j=i+1;
        }
        if(end[i]==' '&&end[i-1]>='0'&&end[i-1]<='9')
        {
            for(k=j,x=0; k<=i; k++,x++) num[x]=end[k];
            //在截取完整的数存放到num中,但是这时的数还是char的,下面用atof函数进行转化
            number=atof(num);//将char型的数转化为double
            if(flag==false)
            {
                number=(-1)*number;
                flag=true;//处理完为负数之后那么把标识又设为不是负数,下一次循环使用
            }
            push_num(number,num_s);
            memset(num,' ',sizeof(num));
            number=0;
        }//遇到的是操作数,进栈;
        else if(end[i-1]==' '&&end[i]<='9'&&end[i]>='0')  j=i;
        //扫描到数字的第一个啦那么把j设置成i一遍循环时候num中截取字符串
        else if(a[i]=='+'||a[i]=='-'||a[i]=='*'||a[i]=='/')
        {
            top=top_num(num_s);
            pop_num(num_s);
            sec=top_num(num_s);
            switch (a[i])  /*对不同运算符进行分别处理*/
            {
            case'+':
                ans=sec+top;
                break;
            case'-':
                ans=sec-top;
                break;
            case'*':
                ans=sec*top;
                break;
            case'/':
                ans=sec/top;
                break;
            }
            pop_num(num_s);
            push_num(ans,num_s);
        }
           /*若遇运算符,则从栈中退出两个元素,
            先退出的放到运算符的右边,后退出的放到运算符左边,
            运算后的结果再进栈*/
        else continue;
    }
    return  top_num(num_s);//栈中只有一个元素啦,返回为结果
}



一、问描述 在控制台下实现一个对算术表达式求值的模拟程序。 二、基本要求 该演示程序具有如下基本功能: (1) 表达式输入,以字符序列的形式从终端输入一个语法正确的数值表达式(float型),且表达式中只含有+、-、*、/、( 、)6 种运算符,输入格式如下: <变量><运算符><变量>……<回车> 例如表达式: 100+(15/3)*2 输入格式为: 100+(15/3)*2<回车> 注意: 输入的表达式中间不含空格。 (2) 表达式结果的输出,输出形式为: <表达式> = <结果> 例如表达式: 100+(15/3)*2 输出形式为: 100+(15/3)*2 = 110 注意: 此处的输出结果为整个表达式的数值结果。 (3) 数据合法性检验 主要是针对原表达式中除数为 0 的情况。 三、界面效果 表达式求值模拟程序 功能菜单: ============== [1] 输入表达式并求值 [0] 退出 ============== 请输入你的选择 (0~1):1 请输入一个表达式 : 100+(15/3)*2 计算结果如下: 100+(15/3)*2 = 110 请输入你的选择 (0~1):0 四、测试数据 (1) 8 = (2) 1+2+3+4 = (3) 88-1*5 = (4) 1024/4*8 = (5) 1024/(4*8) = (6) (20+2)*(6/2) = (7) 3-3-3 = (8) 80/(9-9) = (9) (6+2*(3+6*(6+6)) = (10) (((6+6)*6+3)*2+6)*2 = 五、实现提示 (1) 设置运算符和操作数辅助分析算符优先关系; (2) 在读入字符序列时,完成运算符和操作数的处理,以及相应运算; (3) 在识别处运算数的同时,要将其字符序列形式转化成 float 型数据形式; (4) 输入的字符序列中,操作数不一定是一位数,可能是多位数,如 16+32 ; (5) 使用 Lab3-1 实现的的 ADT 基本操作完成本次作业 ; (6) 在程序中会用到两类:操作数和运算符,分别为 float 型数据和字符型数据, 思考在同一个程序中如何处理两类不同的数据类型? (7) 算符之间的优先关系参考课本 P53 页表 3.1 。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值