数据结构课程实验二:运用栈实现表达式求值

实验日期:2022-10-31

目录

一、实验目的

二、实验内容

三、实验提示 

四、实验思路

1.整体思路

2.详细思路

五、实验思考过程

六、代码运行截图

1.个位数的运算

2. 多位数的运算

3.非法输入的情况 

七、程序流程图

八、个人收获及实验总结

1.个人收获 

2.实验总结

九、实验源代码


一、实验目的

1、掌握栈与队列的定义;

2、掌握掌握栈与队列的基本操作。

二、实验内容

1.验证某算术表达式的正确性,若正确,则计算该算术表达式的值。

(1)、从键盘上输入表达式的值。

(2)、分析该表达式是否合法:

(a)是数字,则 判断该数字的合法性。若合法,则压入数据存放堆栈。

(b)是规定的运算符,则根据规则进行处理。在处理过程中,将计算该表达式的值。

(c)若是其它字符,则返回错误信息。

(3)、若上述处理过程中没有发现错误,则认为该表达式合法,并打印处理结果。

三、实验提示 

表达式的计算过程:

1、变量直接输出;

2、遇到“(”,无条件入栈;

3、输入运算优先数是2,3,4,5,时,如栈空则入栈,否则,将其与栈顶元素进行比较,如果优先级大,就进栈,否则就退栈输出; 

4、如遇“)”,同时栈顶元为“(”,则退栈,抹去括号。

四、实验思路

1.整体思路

首先将用户输入一整串的运算式存入字符exp数组然后通过栈进行一系列处理得到运算结果

2.详细思路

(1)输入运算式定义并初始化数字栈和字符栈分别用于存储输入数据中的数字和运算符

(2)若为数字则压入数字栈此处与众不同的是通过归并的思想可实现多位数的运算

(3)若为运算符则首先进行与栈顶元素优先级的判断若优先级低于栈顶元素则运算符栈栈顶元素弹出同时弹出数字栈两个栈顶元素进行运算得到的运算结果压入数字栈若优先级高于栈顶元素则直接入栈若优先级相等则栈顶元素出栈

(4)若为空格则直接跳过

(5)若为其他情况则判为非法字符输入程序停止运行

(6)经过一系列的运算出栈入栈的操作后最终获取数字栈中的数据可得运算结果

五、实验思考过程

此次实验代码经过反复思考通过三次更改得到了最后的版本

  1. 前两次最核心的思想便是将输入的中缀表达式先转化为后缀表达式然后在对后缀表达式进行结果求值的运算
  2. 处理中缀表达式需要两个字符数组,一个读入字符串(a),另一个存放后缀表达式的字符串,另外需要一个栈对符号进行入出栈操作,这个栈称为”字符栈“(CharStack),将处理后的结果存入另一个字符数组中(b),该字符数组(b)中所存字符串被称为后缀表达式。而处理后缀表达式则需要上述字符数组b和一个数字栈完成运算
  3. 前两种版本存在的问题受ASCII数字字符的判断条件限制其仅能够实现对个位数的运算对于多位数则很难判别出其具体数位构成功能大大缩减
  4. 由于存储多位数时一个数组单元格只能存一位数字所以在现版本中采用了一种归并的思想若输入的字符数组第一位和第二位数后一位均为数字则不断归并至后一位元素不为数字为止将这些数按照位数权加此时得到的便是完整的多位数如下

5.此程序总体功能:可实现多位整数的运算但由于时间有限,尚未想出浮点数的求值算法

 6.现版本输入方式的选取:整体输入此方式可通过直接建立一个字符数组利用gets方法或scanf函数获取输入的数据逐个输入则需要使用getchar()进行逐个读取整体输入相对逐个输入更加直观

7.优先级判断函数的思路在前两个版本中我采用的是比较直白的if语句列出可能的情况进行结果输出现版本受网上资料的启发采用以二维数组存储优先列表将可能的7*7种情况列出利用switch语句输出对应的结果更加直观

8.提高程序健壮性的体现

(1)整个程序运算中无可避免的出现了除法运算因此在求值函数operate中当theta为“/利用if语句判断若除数为0,则提示用户输入错误并结束程序

(2)运算式输入的字符串出现非法字符、空格的处理在evaluateExpression函数中同样采用if语句进行判断若为空格则使用continue跳过若不为字符数字和空格则输入为非法字符程序将提示输入错误

(3)在对栈操作方面的体现初始化栈时对内存是否申请成功进行判断入栈时对是否栈满进行判断出栈和获取栈顶元素时对是否栈空进行判断

9.程序运行界面友好的体现输入输出方面进行一系列的提示以输入为更重要受程序算法限制输入的运算式必须以“#结尾否则无法准确的识别

六、代码运行截图

1.个位数的运算

2. 多位数的运算

3.非法输入的情况 

七、程序流程图

八、个人收获及实验总结

1.个人收获 

(1)通过此次实验对栈这一数据结构有了更深一步的认识也更加熟悉了栈的一系列表示和实现操作

(2)通过表达式求值方法的探索深入理解了“后缀表达式”的含义并掌握了将中缀表达式转化为后缀表达式的方法这同样也是表达式求值的重要算法

(3)在功能扩展方面将个位数的运算提升到多位数的运算很好的学习并利用了归并的思想其与递归思想有很多类似之处

(4)对C语言和C++两种编程语言差别的认识又多了一个细微的收获

最初采用的是这种函数参数即函数参数带&符号与教材上的伪代码一致但在实际运行时Xcode编译器却一直对此处报错结果如下)。查阅资料后发现,&为引用符它是C++的内容当需要对原变量进行修改时可以给参数添加&。但部分C语言编译器是无法适用的

问题的解决思考后发现这种函数参数加&的功能与C语言指针的功能十分相似因此将函数参数定义为指针类型即可很好的解决此问题另一方面也可以直接将文件后缀名从.c

改为.cpp,即改成C++文件同样可以解决此问题

2.实验总结

表达式求值这一算法核心在于对运算式的运算过程利用栈来实现是很方便的因此便涉及运算符优先级的判断和如何对进行有序的运算的思考此外对功能是否可以扩展的思考同样极为重要其可大大提高程序的实

九、实验源代码

#include 
#include 
#include 
//利用宏常量定义最大存储空间
#define Maxsize 100

//定义数字栈
typedef struct Stack
{
    int *top;
    int *base;
    int stacksize;
}sqstack;

//构造空栈
void initStack(sqstack *s)
{
    //申请内存空间
    s->base=(int *)malloc(Maxsize*sizeof(int));
    
     if(!s->base)
     {
        printf("栈初始化失败!\n");
        return;
     }
    //初始化,此时栈空
    s->top=s->base;
    s->stacksize=Maxsize;
    return;
}

//入栈操作
int push(sqstack *s,int e)
{
    if(s->top-s->base==s->stacksize)
    {
        printf("栈满!\n");
        return 0;
    }
    *s->top++=e;
    return 1;
}

//出栈操作
int pop(sqstack *s,int  *elem)
{
    if(s->base==s->top)
    {
        printf("栈空!\n");
        return 0;
    }
  *elem=*--s->top;
    return 1;
}

//获取栈顶元素
int getTop(sqstack *s)
{
    if(s->base==s->top)
    {
        printf("栈空!无法获取元素!\n");
        return 0;
    }
    return *(s->top-1);
}

//判断运算符优先级
char Precede(char theta1,char theta2)
{
    int i = 0,j = 0;
    //对7*7种可能性进行分列,得到最后的运算符对比结果
    char pre[7][7]={// +   -   *   /   (   )   =
                     {'>','>','<','<','<','>','>'},
                     {'>','>','<','<','<','>','>'},
                     {'>','>','>','>','<','>','>'},
                     {'>','>','>','>','<','>','>'},
                     {'<','<','<','<','<','=','0'},
                     {'>','>','>','>','0','>','>'},
                     {'<','<','<','<','<','0','='}};
                
    switch(theta1){
        case '+': i=0; break;
        case '-': i=1; break;
        case '*': i=2; break;
        case '/': i=3; break;
        case '(': i=4; break;
        case ')': i=5; break;
        case '=': i=6; break;
    }
    switch(theta2){
        case '+': j=0; break;
        case '-': j=1; break;
        case '*': j=2; break;
        case '/': j=3; break;
        case '(': j=4; break;
        case ')': j=5; break;
        case '=': j=6; break;
    }
    return(pre[i][j]);
}

//对操作数进行运算
int Operate(int a,char theta,int b)
{
    switch(theta){
        case'+':return a+b;
        case'-':return a-b;
        case'*':return a*b;
        case'/':
            //考虑分母为0的情况,若除数为零返回错误提示
                if(b!=0)
                return a/b;
                else
                {
                    printf("分母为0,无意义!\n");
                    exit(0);
                }
    }
    return 0;
}

//判断是否为运算符,若是运算符返回1,若不是返回0
int isChar(char c)
{
    switch(c){
        case '+':
        case '-':
        case '*':
        case '/':
        case '(':
        case ')':
        case '=':
                 return 1;
         default:
                 return 0;
    }
}

//核心算法,进行运算式运算
int evaluateExpression(char exp[])
{
    //定义运算数栈、运算符栈
    sqstack OPND,OPTR;
    
    /* a,b,theta用于Operate函数
    X用于存放多余的出栈字符
    X1,X2用于归并 */
    int  a,b;
    int x,X1,X2,theta;
    
    //读取字符的变量
    char ch;
    //指向存放表达式数组的下标指针(其实不是真正的指针,而是数组下标)
    int i=0;
    //建立并初始化运算数栈OPND
    initStack(&OPND);
    //建立并初始化运算符栈OPTR
    initStack(&OPTR);
    //先将“=”号压入OPTR栈(表达式也必须以“=”结束)
    push(&OPTR,'=');
    //ch读取字符数组第一个元素,并将指针i后移一位
    ch=exp[i++];
    
    //表达式没有扫描完毕或OPTR的栈顶不为“=”
    while(ch!='='||getTop(&OPTR)!='=')
    {
        if(isChar(ch)) //判断ch是否为操作符
        {
            //比较ch的与OPTR栈顶元素的优先级
            switch(Precede(getTop(&OPTR),ch))
            {
                case'<':
                        push(&OPTR,ch);
                    //读取下一位字符并将指针向后偏移一位
                        ch=exp[i++];
                        break;
                
                case'>':
                    /*若栈顶操作符优先级更大,则弹出运算符栈顶运算符及数字栈栈顶两个运算数
                     */
                        pop(&OPTR,&theta);
                        pop(&OPND,&b);
                        pop(&OPND,&a);
                    //运算结果压入数字栈
                        push(&OPND,Operate(a,theta,b));
                        break;
                
                case'=':
                    //若优先级相当,则将运算符栈栈顶元素弹出
                        pop(&OPTR,&x);
                    //读取下一位字符并将指针向后偏移一位
                        ch=exp[i++];
                        break;
            }
        
        }
        
        //判断是否为数字字符
        else if(isdigit(ch))
        {
            //将字符形式转化为数字
            X1=ch-'0';
            push(&OPND,X1);
            X2=X1;
            //读取下一位字符并将指针向后偏移一位
            ch=exp[i++];
            //归并思想,实现多位数运算
            while(isdigit(ch)) //判断下一位是否还是数字
            {
                X1=ch-'0';
                X2=10*X2+X1; //归并至X2
                pop(&OPND,&x);
                push(&OPND,X2);
                ch=exp[i++]; //读取下一位字符并将指针向后偏移一位
                
            }
        }
        
        else if(ch==' ') //判断是否为空格
        {
            while(ch==' ') //跳过若干个空格
            {
                ch=exp[i++]; //忽略空格,直接取下一位
            }
            
        }
        
        else //若不是上述三种情况之一,则为非法字符
        {
            printf("Input has illegal characters!\n");
            printf("Please enter again.\n");
            exit(0); //返回错误提示
        }
    }
    return(getTop(&OPND)); //最后返回操作数栈顶为运算结果
}

int main()
{

 char exp[100]; //定义一个字符数组用于存储表达式
  int result;
  printf("请输入运算式(运算式尾部务必加上‘=’):\n");
    gets(exp);
  result=evaluateExpression(exp);
  printf("运算式及运算结果如下:\n");
  printf("%s%d\n",exp,result);
  
    return 0;
}

分享结束,感谢观看!!!

你可能感兴趣的:(实验报告,数据结构,数据结构,java,c5全栈,散列表,链表)