实验三  算术表达式求值(必做,设计性实验)
 * 
实验目的
熟练掌握栈的基本操作,深入了解栈的特性,能在实际问题的背景下灵活运用他们,并加深对这种结构的理解。
 * 
实验内容
设计一个程序,演示用算符优先法对算术表达式求值的过程。以字符序列的形式从终端输入语法正确的、不含变量的整数表达式。利用教科书表3.1给出的算符优先关系,实现对算术四则运算混合运算表达式的求值,并仿照教科书的例子3-1演示在求值中运算符栈、运算数栈、输入字符和主要操作的变化。测试数据可以选择例子3-1的算术表达式 3*(7-2),或自选。
 * 
数据结构定义
    (说明你算法中用到的数据结构、数据类型的定义)
栈是一种只能在一端进行插入或删除操作的线性表。表中允许插入、删除操作的一端称为栈顶。栈顶的当前位置是动态的,栈顶的当前位置由一个称为栈顶指针的位置指示器指示。当栈中没有元素时,为空栈,栈的插入操作和删除操作通常称为进栈和出栈。栈的主要特点是后进先出。
Typedef struct{   SElemType *base; SElemType *top; Int stacksize; }SqStack; 
stacksize表示栈当前可使用的最大容量。Base时栈底指针,top作为栈顶指针。
 * 
算法思想及算法设计
    (先文字说明算法的思想,然后给出类C语言算法)
为实现算符优先算法,我们使用两个工作栈,,一个称作OPTR,用以寄存运算符;另一个称作OPND,用以寄存操作数或者运算结果,依次读入每个字符,若操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先权后在操作,直到整个表达式求值完毕。
void GetExpressionValue(){  SqStack OPTR,OPND;  SElemType result;//返回最后结果 
 InitStack(&OPTR);  InitStack(&OPND);  Push(&OPTR,'#');//将结束符置于操作符的底端   
 printf("请输入算术表达式:\n");  char c = getchar(); 
 while(c!='#'||GetTop(&OPTR)!='#'){//当*c=='#'&&栈顶字符=='#'的时候 
  if(isdigit(c)){//如果是数字的话将其转化为数字 然后入操作数栈    int data[10];    int i,num; i = 
num =0;//num是一个中间数 用于将字符串中的数字转化为整数然后入栈 i用于将字符串中的字符存入data数组 
   while(isdigit(c)){     data[i] = c-'0'; i++; c = getchar();    }    for(int 
j=0;j<i;j++){     num = num*10+data[j];    }    Push(&OPND,num); 
  }else{//如果是字符的话将其入操作符栈    SElemType a,b,theta;//a b theta是用来返回操作数栈和操作符栈里的元素的 
   switch(Priority(GetTop(&OPTR),c)){//比较即将入栈的字符与栈顶 操作符的优先级关系     case 
'<':Push(&OPTR,c);        c = getchar();        break;     case 
'>':Pop(&OPND,&b);        Pop(&OPND,&a);        Pop(&OPTR,&theta); 
       Push(&OPND,Reckon(a,theta,b));        break;//将结果入栈     case 
'=':Pop(&OPTR,&theta);        c = getchar();        break;//说明括号相遇 删除栈内括号即可 
    default:break;    } }}  Pop(&OPND,&result);  printf("结果是:%d",result); } 
 * 
实验代码
    (即C语言程序)
#include<stdio.h> #include<stdlib.h> #define OK 1 #define ERROR 0 #define 
STACKINCREMENT 5 #define STACK_INIT_SIZE 10 typedef char SElemType; typedef int 
Status; typedef struct{  SElemType *base;//栈底指针  SElemType *top;//栈顶指针  int 
stacksize;//当前已经分配的存储空间 }SqStack; char prior[7][7]={
{'>','>','<','<','<','>','>'},{'>','>','<','<','<','>','>'},{'>','>','>','>','<','>','>'}, 
      {'>','>','>','>','<','>','>'},{'<','<','<','<','<','=','!'},{'>','>','>','>','!','>','>'}, 
      {'<','<','<','<','<','!','='}};//定义算符之间优先关系的二维数组 //构造一个存放char型数据的空栈 
Status InitStack(SqStack *s){  s->base = (SElemType 
*)malloc(STACK_INIT_SIZE*sizeof(SElemType));  if(!s->base) return ERROR; 
 s->top = s->base;//栈中元素个数为0  s->stacksize = STACK_INIT_SIZE;  return OK; } 
//入栈 Status Push(SqStack *s,SElemType e){  if(s->top-s->base>=s->stacksize){ 
  s->base = (SElemType 
*)realloc(s->base,(STACKINCREMENT+s->stacksize)*sizeof(SElemType)); 
  if(!s->base) exit(0);   s->top = s->base+s->stacksize;   s->stacksize += 
STACKINCREMENT;  }  *s->top++ = e;  return OK; } //出栈 Status Pop(SqStack 
*s,SElemType *e){  if(s->base==s->top){   printf("空栈!\n");   return ERROR;  } 
 *e = *--s->top;  return OK; } //得到栈顶元素 SElemType GetTop(SqStack *s){  return 
*(s->top-1); } //确定输入的字符如果是操作符的话判断在二维数组中的下标 若是数字的话就另外与操作符区分开 便于在输入表达式时是入哪个栈 int 
Index(char c){  switch(c){   case '+': return 0;   case '-': return 1;   case 
'*': return 2;   case '/': return 3;   case '(': return 4;   case ')': return 
5;   case '#': return 6;   default:  return 7;  } } //判断优先级,返回大小 < > = ! char 
Priority(char a,char b){  int x,y;  x = Index(a); y = Index(b);  if(x!=7&&y!=7) 
  return prior[x][y];  else   return '!'; } //简单表达式求值 int Reckon(int a,char 
theta,int b){  switch(theta){   case '+':return a+b;   case '-':return a-b; 
  case '*':return a*b;   case '/':return a/b;  } } //判断是字符是否是数字 Status 
isdigit(char ch){  if(ch>='0'&&ch<='9') return OK;  return ERROR; } //算术表达式求值 
void GetExpressionValue(){  SqStack OPTR,OPND;  SElemType result;//返回最后结果 
 InitStack(&OPTR);  InitStack(&OPND);  Push(&OPTR,'#');//将结束符置于操作符的底端   
 printf("请输入算术表达式:\n");  char c = getchar(); 
 while(c!='#'||GetTop(&OPTR)!='#'){//当*c=='#'&&栈顶字符=='#'的时候 
  if(isdigit(c)){//如果是数字的话将其转化为数字 然后入操作数栈    int data[10];    int i,num;    i = 
num =0;//num是一个中间数 用于将字符串中的数字转化为整数然后入栈 i是用于将字符串中的字符存入data数组 
   while(isdigit(c)){     data[i] = c-'0';     i++;     c = getchar();    } 
   for(int j=0;j<i;j++){     num = num*10+data[j];    }    Push(&OPND,num); 
  }else{//如果是字符的话将其入操作符栈    SElemType a,b,theta;//a b theta是用来返回操作数栈和操作符栈里的元素的 
   switch(Priority(GetTop(&OPTR),c)){//比较即将入栈的字符与栈顶 操作符的优先级关系     case 
'<':Push(&OPTR,c);        c = getchar();        break;     case 
'>':Pop(&OPND,&b);        Pop(&OPND,&a);        Pop(&OPTR,&theta); 
       Push(&OPND,Reckon(a,theta,b));        break;//将结果入栈     case 
'=':Pop(&OPTR,&theta);        c = getchar();        break;//说明括号相遇 删除栈内括号即可 
    default:break;    }   }  }  Pop(&OPND,&result);  printf("结果是:%d",result); } 
 * 算法测试结果 
    (说明测试数据,粘贴实验结果图)
实验数据:3*(5+2)  9/(5+2)
 
 
 * 分析与总结 
    (1)算法复杂度分析及优、缺点分析
        (说明你编写算法的复杂度,算法的优点和缺点有哪些)
数据压缩存储栈,其操作主要有: 
建立栈int Push(SeqStack *S, char x) 入栈int Pop(SeqStack *S, char x) 出栈。 
以上各操作运算的平均时间复杂度为O(n),其主要时间是耗费在输入操作。
    (2)实验总结
        (说明你怎么解决实验中遇到的问题,有什么收获)
通过这次实验,让我复习了栈的知识,增强的我的c语言编程能力。
做什么都需要耐心,做设计写程序更需要耐心。一开始的时候,我写函数写的很快,可是等最后调试的时候发现错误很隐蔽,就很费时间了。后来我先在纸上构思出函数的功能和参数,考虑好接口之后才动手编,这样就比较容易成功了。