栈和队列的关键算法实现及性能分析

实验题目:栈和队列的关键算法实现及性能分析

一、实验目的

1. 能够运用高级程序设计技术实现栈和队列及其关键算法(实验1、2)

2. 能够通过对比分析的方法分析关键算法性能的影响规律(实验12

3. 具体任务参加各个实验的任务书

二、实验原理

1.栈的基本原理

(1)栈它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

(2)基本算法:

进栈(PUSH)算法:

①若TOP≥n时,则给出溢出信息,作出错处理(进栈前首先检查栈是否已满,满则溢出;不满则作②);

②置TOP=TOP+1(栈指针加1,指向进栈地址);

③S(TOP)=X,结束(X为新进栈的元素);

退栈(POP)算法:

①若TOP≤0,则给出下溢信息,作出错处理(退栈前先检查是否已为空栈, 空则下溢;不空则作②);

②X=S(TOP),(退栈后的元素赋给X):

③TOP=TOP-1,结束(栈指针减1,指向栈顶)

(3)定义stack的简单代码:

stack<int> sta;

入栈:sta.push(x);

出栈:sta.pop();

判断栈的大小: sta.size();

判断栈是否为空:sta.empty();

 (4)队列的基本原理

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除。

(5)队列基本算法

初始化队列:Init_Queue(q) ,初始条件:队q 不存在。操作结果:构造了一个空队;

入队操作: In_Queue(q,x),初始条件: 队q 存在。操作结果: 对已存在的队列q,插入一个元素x 到队尾,队发生变化;

出队操作: Out_Queue(q,x),初始条件: 队q 存在且非空,操作结果: 删除队首元素,并返回其值,队发生变化;

读队头元素:Front_Queue(q,x),初始条件: 队q 存在且非空,操作结果: 读队头元素,并返回其值,队不变;

判队空操作:Empty_Queue(q),初始条件: 队q 存在,操作结果: 若q 为空队则返回为1,否则返回为0。

2.关键算法设计原理及性能影响因素分析

①顺序栈是静态分配的,而链栈则是动态分配的,二者存储结构不同。链栈可以将很多零碎的空间利用起来,容量可变,节省空间,顺序栈则固定内存空间,容量不变。
    顺序栈查询速度快,链栈添加删除数据更快。

尽管顺序栈简单、存储密度高。但是即使栈的长度很长,也还是可能发生上溢。当栈的容量不固定时,必须设置栈的长度以使其可以容纳更多的元素,容易浪费空间。

当栈的容量不固定时,必须设置栈,使其容纳最多的数据元素,这样会浪费很对存储空间,也可能产生上溢出现象,采用链式存储结构就不会产生类似的问题。

因此两种栈的适用范围受很多因素的影响。

②队列遵循先进先出的原则,这是和栈不同的地方。整个顺序队列在数据不断地进队出队过程中,在顺序表中的位置不断后移。这将导致顺序队列之前的数组存储空间将无法再被使用,造成了空间浪费;而且如果顺序表申请的空间不足够大,则直接造成程序中数组 a 溢出,产生溢出错误。

三、实验方案设计

1.存储方案设计

分析:(1)栈:向一个栈插入新元素即入栈)时是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素(即出栈)时,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。这两个操作都是在栈顶元素上进行操作,也就是“先进先出”。首先要定义栈顶最大容量,如果元素较多时要定义一个容量较大的栈,用于储存数量规模未知的元素,并防止元素溢出。所以在十进制转二进制的实验中,设计一个顺序栈来进行后续实验。

  1. (2)队列:队列中数据的进出要遵循 "先进先出" 的原则,即最先进队列的数据元素,同样要最先出队列。所以在考虑排队问题时优先选择队列。而队列的特点就能满足舞伴的选择问题。先定义两个较大的队列长度,分别用于存储男性和女性的信息。再进行入队操作,让先进入队列的元素(即舞者)优先和另一队列排在前面的进行两两配对。从而达到目的。

3.算法的设计和实现

     栈:

(1)、栈的初始化: Status InitStack(SqStack &S){

    S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType))

    if(!S.base) exit(OVERFLOW); //存储分配失败

    S.top = S.base;

    S.stacksize = STACK_INIT_SIZE;

    return OK;

}


(2)、栈顶元素插入: Status Push(SqStack &S, SElemType &e){

    if(S.top - S.base >= S.stacksize){  //栈满,追加存储空间

        S.base = (SElemType *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

        if(!S.base) exit(OVERFLOW); //存储分配失败

        S.top = S.base + S.stacksize;

        S.stacksize += STACKINCREMENT;

    }

    * S.top++ = e;

    return OK;

}


(3)、栈顶元素获取:Status GetTop(SqStack &S, SElemType &e){

    if(S.top == S.base) return ERROR;

    e = *(S.top - 1);

    return OK;

}

(4)、栈的销毁: Status DestroyStack(SqStack &S){

     free(S.base);

     S.base = NULL;

    S.top = NULL;

     S.stacksize = 0;

    return OK;

 }

(5)判断s是 否为空栈

Empty(s)

{

if(s.top==-1)

return true;

return false;

}

队列:

空队列

  1. .创建空队列

LinkQueue InitQueue( )

{

LinkQueue Q;

Q. front=Q. rear= (QueuePtr) malloc(LENG);

Q. front->next=NULL;

return Q;

}

(2)入队列

EnQueue(q, e)//进入队列

{

if(Q->rear>=MAXQSIZE)

return ERROR;

Q->data[Q->rear]=e;

Q->rear++;

return OK;

}

(3)出队列

DeQueue(q,e)

{

if(q->rear==q->front)

return ERROR;

e=q->data[q->front];

q->front++ ;

return OK;

}

(4)判断q是否为空队列

QueueEmpty(Q)

{

if(Q->rear==Q->front)

return OK;

return ERROR;

}

(5)销毁队列

DestroyQueue (I inkQueue &Q)

{

while(Q.front)

{

Q.rear=Q.front-> >next;

free(Q.front);

Q.front=Q.rear;

}

return OK;

}

3.测试方案

(1)栈的相关测试

①入栈操作(先对栈进行初始化)

测试数据:99 88 77 66 55 44 33 22 11

预期结果:栈内存储了99 88 77 66 55 44 33 22 11共9个元素。

②判断栈是否为空。

 测试数据:&S

 预期结果:false

③在确定栈非空之后,判断栈的长度( 调用StackLength( SqStack S ) )

 测试数据:&S
  预期结果:9

④选择元素进行入栈操作。并重新判断栈的长度。

 测试数据:123 789

 预期结果:栈内元素为99 88 77 66 55 44 33 22 11 123 789

           且对于栈长度判断返回11

⑤进行出栈操作,出栈4个元素,并重新判断栈的长度。

 测试数据:&S

 预期结果:789 123 11 22  

  此时返回栈的长度为7

(2)队列的测试数据

①通过入对操作进行数据的输入(创建一个新的队列后)

 测试数据:12 23 34 45 56 67 78 89 90

 预期结果:队列内存储了12 23 34 45 56 67 78 89 90共9个元素

②判断栈是否为空。(通过QueueEmpty(Q)函数进行判断)

预期结果:ERROR

③在进行出队操作后进行取队首元素操作,结果应该只有一个出队

测试数据:&Q

预期结果:12 23  对中所剩元素个数为8

实验报告

  • 概述

本次内容是与栈和队列相关的三个实验题。包括对顺序栈和队列的操作,比如判断栈是否为空、入栈、删除栈顶元素、加入队列、出队列等内容。在本次实验中,首先建立一个容量较大的空栈,然后输入数据存入栈中,通过不同的元素的输入,实现入栈、出栈等操作。入栈和出栈的操作类似,在进行完这两个操作时,都是在栈顶元素上进行操作的,即后进入的元素,同时记录数据元素的长度的标示符也要跟着改变。队列和栈存储和释放元素的方式是不同的,队列所遵循的是先进先出原则。队列是只允许在一端进行插入操作,在另一端进行删除操作的线性表。允许插入的一端为队尾,允许删除的一端为队头,先进先出,相邻元素具有前驱与后继关系。考虑到十进制转二进制的要求以及特点,选择用顺序栈来实现,而舞伴分配问题类似于排队问题,选择用队列来实现。顺序栈连续存储,有固定的内存空间,空间利用率高,但正是由于是一段连续的存储空间,所以不方便数据的增删。只允许在表的前端进行删除操作,而在表的后端进行插入操作。当队列为空时,有front=rear,而当所有队列空间全占满时,也有front=rear。为了区别这两种情况,规定循环队列最多只能有MaxSize-1个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。在实际的物理空间中,顺序队列的数据集中存储,链队列的数据分散存储。

二、实验过程

1.调试分析

(1).PTA代码里由于缺少缺少头文件,导致printf函数无法使用。

(2).由于在输入代码时误将赋值操作符写成了等于操作符,导致运行结果错误。

                     

(3)返回的大小写错误,在c语言中,大小写分别是两个不同的变量,大小写的不同使其蕴含的意思也完全不相同,于是在该函数的返回中返回来“ok”这个不存在的值,所以会进行报错,解决方法为将“ok”改为“OK”。

       

2.测试过程

对于不同的实验应该有不同的输入,所以将实验分为三个大部分,一个实验对应一个部分,并且每一部分应有不同的测试数据。

实验一:十进制转二进制(顺序栈设计和应用)

该实验主要考察对顺序栈的操作部分,由于只涉及到进制的转换,所以可进行多组数据进行测试。

第一个测试数据:12

运行结果如下图:

      

第二个测试数据:26

运行结果如下图:

  

由以上两个图可以得到,运行结果和预期结果完全相符,所以该程序测试完毕且无误。

实验二:jmu-ds-舞伴问题

舞伴问题主要是利用入队列、出队列的操作,通过创造一个空的队列,再让男士和女士分别进入队列,再进行出队列,从而达到最终目的。但由于情境的要求,故而需要大量的数据完成。

第一个测试数据:

              共7个元素,分别为 赵F  钱M  孙M  李M  周F  吴M  郑F

运行结果如下图:

      

第二个测试数据:

  共4个元素,分别为张F 蒋F 沈M 韩M

运行结果如下图:

      

  

三、评价分析

1.实验结果分析

实验过程中,对程序的调试基本达到了预期的效果,对于栈的实验中,由于初始定义的栈的空间较小,导致了第一次运行时发生了“溢出”。后期增大的栈的空间,在保证不发生“溢出”的前提下,成功的完成了入栈操作。

调试舞伴问题的实验时,成功的实现了预期的目标。只是选择用于测试的数据不够多样,程序还有改进的空间。

2.算法性能评价

顺序栈的算法较为单一,所以重点在于入栈和出栈的算法进行优化和改善,与队列不同的是,入栈和出栈不必改变和移动其他的元素。但是由于顺序栈的连续空间大小已定,所以可以用链栈来解决空间不连续的问题。

     

      

  • 总结与体会

    对于栈和队列都属于线性表问题,但是栈的特点是“后进先出”,在栈顶操作。队列是“先进先出”,两者各有所长。顺序栈的结构比较简单,逻辑上相邻的元素在物理上也是相邻的。而且在不使用指针的前提下节省了很多的储存空间。但是需要考虑到元素入栈时是否溢出的问题。队列和生活中的排队很像,两者类比可以更快地理解。有了线性表的实验原理基础,再经过这次实验,对于栈和队列的相关代码已基本熟悉,算法知识得到了复习与巩固。在写代码的过程与调试中,在解决问题过程中,丰富了个人编程的经历和经验,提高了个人解决问题的能力。但是在编写程序的过程中关于c语言的指针方面的内容还不能得心应手,这也影响了对数据结构这门课的理解和掌握。所以还要多练习,达到熟能生巧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值