实验三栈的基本操作(必做,设计性实验)
- 实验目的
熟练掌握栈的抽象数据类型,能在相应的应用问题中正确选用它,熟练掌握栈的实现方法(顺序和链式),两种存储结构和基本操作的实现算法,注意空和满的判断条件及它们的描述方法。深入了解栈的特性,能在实际问题的背景下灵活运用他们,并加深对这种结构的理解。
- 实验内容(选其中之一书写实验报告)
选择合适的存储结构(顺序栈或链式栈)表示栈,给出其定义,在上述存储结构上实现栈的基本操作:初始化、置栈空、入栈、出栈、取栈顶元素等。然后完成下述任务之一:
(1)用栈实现括号匹配的检验
(2)用栈实现形如a+b@b+a#的中心对称的字符序列的检验。
(3)设计一个程序,演示用算符优先法对算术表达式求值的过程。以字符序列的形式从终端输入语法正确的、不含变量的整数表达式。利用教科书表3.1给出的算符优先关系,实现对算术四则运算混合运算表达式的求值,并仿照教科书的例子3-1演示在求值中运算符栈、运算数栈、输入字符和主要操作的变化。测试数据可以选择例子3-1的算术表达式 3*(7-2),或自选。
- 数据结构定义
(说明你算法中用到的数据结构、数据类型的定义)
数据结构:一种只能在一端进行插入或删除操作的线性表—栈。表中允许插入、删除操作的一端称为栈顶。栈顶的当前位置是动态的,栈顶的当前位置由一个称为栈顶指针的位置指示器指示。当栈中没有元素时,为空栈,栈的插入操作和删除操作通常称为进栈和出栈。栈的主要特点是后进先出。采用的存储结构是链式存储。
数据类型的定义:stacksize表示栈当前可使用的最大容量。Base时栈底指针,top作为栈顶指针。
typedef struct{
SElemType *base;//栈底指针
SElemType *top;//栈顶指针
int stacksize;//当前已经分配的存储空间
}SqStack;
- 算法思想及算法设计
(先文字说明算法的思想,然后给出类C语言算法)
(1)算法思想
①首先定义一个算符优先关系的二维数组,为实现算符优先算法,使用两个工作栈,一个称作OPTR,用以寄存运算符,另一个称作OPND,用以寄存操作数或者运算结果;②依次读入每个字符,若操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先权后再操作,若高于OPTR栈的栈顶运算符优先权则入栈,若低于OPTR栈的栈顶运算符优先权则将OPTR栈的栈顶运算符和OPND栈的两个操作数出栈并将运算结果入OPND栈,若与OPTR栈的栈顶运算符优先权相等则将栈内括号出栈即可,直到整个表达式求值完毕;③最后输出算术表达式结果。
(2)类C语言算法
OperandType EvaluateExpression(){
//算术表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈
InitStack(OPTR); Push(OPTR,'#');
InitStack(OPND); c = getchar();
while(c!='#'||GetTop(OPTR)!='#'){
if(isdigit(c)){ //如果是数字的话将其转化为数字然后入操作数栈
i = num =0; //num用于将字符串中的数字转化为整数然后入栈
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
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语言程序)
详见电子版
- 算法测试结果
(说明测试数据,粘贴实验结果图)
|
- 分析与总结
(1)算法复杂度分析及优、缺点分析
(说明你编写算法的复杂度,算法的优点和缺点有哪些)
算法复杂度分析:
InitStack(SqStack *s), Push(SqStack *s,SElemType e), Pop(SqStack *s,SElemType *e),
GetTop(SqStack *s), Index(char c), Priority(char a,char b),
Reckon(int a,char theta,int b), isdigit(char ch)
算法时间复杂度为O(1)
main()
时间复杂度为O(n*i)(i为算数表达式中最大数的位数)
优点:
①方便求解复杂表达式:栈提供了一种便捷的数据结构,可以嵌套存储操作数和运算符,使得复杂的算术表达式的求解变得相对简单明了。
②支持多层嵌套括号:栈的先进后出特性使得我们可以处理多层嵌套的括号,并能够正确计算出表达式的值。
③减少重复计算:在利用栈求值时,可以通过对操作数进行运算并将结果存储在栈中,避免了重复计算,提高了效率。
④灵活性强:通过使用栈,我们可以容易地扩展算术表达式的解析能力,例如支持更多的运算符、函数等。
缺点:
①内存消耗较大:使用栈来解决算术表达式的求值需要额外的内存空间存储栈元素,这对于大型表达式可能导致较大的内存消耗。
②需要预处理和转换:在使用栈求解算术表达式之前,通常需要对中缀表达式进行转换为后缀表达式的操作,这增加了实现算法的复杂性和开销。
③不适用于大型表达式:当算术表达式非常大或者括号嵌套层数过多时,使用栈求解可能导致栈的溢出或者内存不足等问题。
④对表达式有一定要求:在使用栈求解算术表达式时,要求表达式的格式和语法正确,否则可能会导致求值结果不准确或报错。
(2)实验总结
(说明你怎么解决实验中遇到的问题,有什么收获)
问题:如何正确判断运算符的优先级并进行正确的转换。
解决办法:可以使用运算符的优先级来建立一个优先级表,并在转换过程中使用该表进行判断。在程序中,定义一个算符之间优先关系的二维数组,确定输入的字符是操作符就判断在二维数组中的下标,从而等运算符之间的优先关系。另外,要注意处理括号的情况,左括号和右括号的优先级相同,将左括号先入栈,若遇到右括号,则左括号出栈。
收获:
①深入理解了栈的特性和用途,学会了栈的基本操作,以及在算术表达式求值中的应用,对算术表达式的求值过程有了更深入的理解,在实践中加深了对栈的认识和掌握。
②提高了解决问题的能力,通过思考和调试找到并解决了不同的问题。
③培养了耐心和细心的精神,因为算术表达式的求值在每个步骤都需要仔细考虑和处理。
④对数据结构和算法的学习也有了更深入的体会。