




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
PAGE1《计算机软件基础》多媒体教程第十二讲第四章数据结构4.3栈和队列4.3.1栈定义只能对始结点(又称始端)进行操作的线性表称为stack(栈,堆栈)。栈的始端称为栈顶,栈的终端称为栈底。栈的主要操作包括push(进栈)和push(出栈或者退栈),它们只涉及到栈顶结点。进栈操作是指在栈顶添加一个结点,使原来的栈顶结点成为栈顶的下一结点。出栈操作是从栈内取出栈顶结点,并使原来的栈顶下一结点成为栈顶结点。 由此可以看到,最先进栈的结点将最后出栈。 因此,栈又被称为先进后出表,记为FILO表(FirstInLastOutList)。栈的基本操作和存储空间测试 设计一个栈,要给予分配一定的空间,因此在进栈或退栈操作时应注意到栈空间的变化情况。 如果在满栈(结点已经占满栈的容量)时执行进栈操作,称为上溢出(overflow)。 如果在空栈(栈内没有结点)时执行出栈操作,称为下溢出(underflow)。 通常根据栈顶指针的变化来判断是否发生上溢出和下溢出。在程序实现时,必须对上溢出和下溢出的情况加以处理,否则将引起出错。栈的存储方式 按照栈的存储方式,可以分为顺序栈和链接栈。 用顺序存储方式实现的栈称为顺序栈,通常可以用数组来构造顺序栈。 用链接存储方式实现的栈称为链接栈,通常可以用链表来构造链接栈。【例4-3.1】栈的应用示例:铁路车厢编组 下图中铁路的右侧是一个车厢的堆栈,规定从上边轨道进入堆栈的操作为P(push),从堆栈退出进入下边轨道的操作为O(pop)。通过一系列的进栈和出栈操作,可以对火车车厢进行重新编组。 例如经过PPO的操作,原来按{1,2,3}编组的车厢被牵引到各条轨道。而经过PPOPOO的操作,原来按{1,2,3}编组的车厢可以重新编组为{2,1,3}的序列。待编组的火车车厢经过PPO操作的车厢编组编组后的火车车厢4.3.2顺序栈数据定义 #define M 1000 #define MAX M-1 /*栈容量 */ #define MIN 0 /*栈底指针值 */ short Stack[M]; /*用数组存放栈 */ short Top=MIN-1; /*栈顶指针,初始为空栈 */进栈函数 voidpush(shortkey)/*结点值*/ { if(Top>=MAX /*overflow */ ||Top<MIN-1)/*非法指针值 */ error(); /*结点进栈,指针加1*/ Stack[++Top]=key; }出栈函数 shortpop(void) { if(Top<MIN/*underflow */ ||Top>MAX)/*非法指针值 */ error(); /*返回栈顶结点,指针减1*/ return(Stack[Top--]); }调用方法示例 push(key);调用方法示例 key=pop();顺序栈存储空间的讨论 上面在函数push和pop中,遇到上溢出(overflow)和下溢出(underflow)时,仅调用了一个出错处理函数error。实际应用中应该能对溢出的情况加以具体处理。下溢出表示程序设计有误,应该检查后加以修改。上溢出表示初始给定的空间太少了。为此,需要重新构造栈。 应用中可能要在同一段存储空间使用一个或几个栈,可以采用同向双栈或者相向双栈。同向双栈 在同一个数组中同向存放A栈和B栈。 A栈指针:TopA, A栈栈底:MIN, A栈容量:L-1。 B栈指针:TopB, B栈栈底:L, B栈容量:MAX。⊙同向双栈出栈的溢出下处理 出栈时需要测试是否发生下溢出(underflow)。问题:程序出错(误操作),须改正。同向双栈进栈的溢出处理 进栈时需要测试是否发生上溢出(overflow)。 若A栈和B栈没有同时满栈,可移动B栈,调整栈容量。 A栈满,B栈未满:B栈向后移动。 B栈满,A栈未满:B栈向前移动。 相向双栈 使用A栈和B栈两个栈,在同一个数组中相向存放。 A栈指针:TopA, A栈栈底:MIN, A栈容量:TopB-1。 B栈指针:TopB, B栈栈底:MAX, B栈容量:TopA+1。相向双栈出栈的溢出处理 出栈时需要测试是否发生下溢出(underflow)。 空栈判据:TopA==MIN-1,TopB==MAX+1 问题:程序出错(误操作),须改正。相向双栈进栈的溢出处理 进栈时需要测试是否发生上溢出(overflow)。 满栈判据:TopA==TopB-1,或TopB==TopA+1 处理:A栈和B栈没有同时满栈时,能够自动调节,而不需要移动A栈或B栈。4.3.3链接栈数据定义 #defineNODEstructnode NODE { short num; NODE *next; }; /*用链表存放栈*/ NODE*Top=NULL; /*栈指针,初始为空栈*/进栈函数 voidpush(shortkey)/*结点值*/ { NODE*node; node=(NODE*)malloc(sizeof(NODE)); if(node==NULL) error(); /*满栈(overflow) */ /*填入结点数据 */ node->num=key; /*插入结点 */ node->next=Top; Top=node; }出栈函数 shortpop(void) { shortkey; NODE*node; if(Top==NULL) error();/*空栈(underflow)*/ key=Top->number; node=Top; /*删除结点 */ Top=node->next; free(node); /*释放结点*/ return(key); }调用方法示例 push(key);调用方法示例 key=pop();空栈管理 在链接栈的进栈函数push()中需要调用malloc()申请空间,出栈函数pop()中需要调用free()释放空间。反复调用malloc()和free(),实际上是增加了与系统打交道的时间。如果次数频繁,将降低程序的运行速度。 通常在实用的软件中可以采用空栈管理的办法。采用空栈管理的做法是集中申请一批存储单元,放在空栈内。需要使用结点(进栈)时从空栈取出结点,用完(出栈)后将结点放回空栈,从而提高软件的效率。 分别设计空栈的出栈函数popfree()和进栈函数pushfree(),其程序参见教材第252页。 从而可以修改链接栈的进栈函数和出栈函数中的相关语句如下:修改进栈函数申请空间的语句 if((node=(NODE*)malloc(sizeof(NODE)))==NULL)改为: if((node=popfree())==NULL)修改出栈函数释放空间的语句 free(node);改为: pushfree(node);【例4-3.2】栈的应用示例:PFE的计算算法描述 PFE(无括号表达式,又称后缀表达式或者逆波兰表达式)的计算,是栈的一种应用。 即K={kj,(j=1,…,n|kj=数字或算术运算符+,-,*,/)}。 以此为程序的输入序列,求PFE的计算结果。 求解PFE计算结果的算法为: for(j=1;j<=n;j++) { if(kj是数字) push(kj) elseif(kj是算术运算符op) { data=pop() result=pop() result=resultopdata push(result) } } 输出result求解过程 以K={-2,4,*,-7,-}为例,用图示法来演示PFE的求解过程。
4.3.4队列定义只能在终端添加终结点和只能在始端删除始结点的线性表称为queue(队列,队)。队的终端称为队尾,队的始端称为队首。队的主要操作包括enqueue(进队)和dequeue(出队)。进队操作是在队尾添加一个结点(队尾结点),并使新加入的结点成为新的队尾结点。出队操作是在队首取出一个结点(队首结点),并使原来队首后面的一个结点成为新的队首结点。 由此可以看到,最先进队的结点必定最先出队。 因此,队又被称为先进先出表,记为FIFO表(FistInFistOutList)。队的基本操作和存储空间测试 类似于栈的情况,设计一个队列,要给予分配一定的空间,因此在进队或出队操作时应注意到队列空间的变化情况。 如果在满队(结点已经占满队列的容量)时执行进队操作,称为上溢出(overflow)。 如果在空队(队内没有结点)时执行出队操作,称为下溢出(underflow)。 通常根据队首指针和队尾指针的变化来判断是否发生上溢出和下溢出。在程序实现时,必须对上溢出和下溢出的情况加以处理,否则将引起出错。队的存储方式 按照队的存储方式,可以分为顺序队和链接队。 用顺序存储方式实现的队称为顺序队,通常可以用数组来构造顺序队。 用链接存储方式实现的队称为链接队,通常可以用链表来构造链接队。【例4-3.3】队列的应用示例:遗产分割案例 某律师受委托执行某富豪留下50亿美元遗产的分割。根据法律,富豪的遗孀为第一继承人,第二继承人是他的子女,第三继承人是他的孙辈,如此往下。 根据富豪的遗嘱,第一继承人可得遗产的60%。第二继承人可在遗产余额的60%进行平均分配,第三继承人可在第二继承人遗产余额的60%进行平均分配,如此往下分割。但是每个继承人得到的遗产份额不得超过上一代继承人份额的60%。同时,遗嘱还规定,每人所得的遗产必须是百万美元的整数,如果有余数,留给下一代分配。如果到最后一代分割后还有余数,将捐给慈善机构。需要律师求出每一代继承人的个人份额,每一代继承人的数量,以及留给慈善机构的金额。 由于富豪后代众多,七嘴八舌,谁也数不清。律师请一位软件工程师帮忙,采用队列来设计了一个统计继承人的算法。 首先,采用一个登记方法,给每个人发一个编号n。然后让每人申报数据D(n,g,b,s),g(generation)是继承顺序,b(brother)是下一个弟弟妹妹的编号,s(son)是长子编号。通过登记并且输入这些数据,从而可获得对应的家谱图。 例如,D(1,1,0,2)表示遗孀的编号为1,她没有兄弟,长子的编号为2。 D(10,2,11,9)表示第二继承人为10,下一个弟弟妹妹为11,其长子为9。D(n,g,b,s)1,1,0,22,2,10,33,3,6,44,4,5,05,4,0,06,3,7,07,3,0,88,4,0,09,3,12,010,2,11,911,2,0,012,3,0,0 然后,设计了一个统计继承人的算法描述为: 令各继承顺序的人数为X(i|i=1,...,g),g为继承顺序总数。 初始令所有的X(i)=0 enQueue(D(1,1,0,2)),X(1)=1 第一继承人进队 while(D(n,g,b,s)=deQueue()) 逐个继承人出队 { if((n0=s)!=0) 申报人有子女 { 找到其长子D(n0,g,b,s),++X(g) enQueue(D(n0,g,b,s)) 子女进队 while((n1=b)!=0) 穷尽其弟弟妹妹 { 找到其弟弟妹妹D(n1,g,b,s),++X(g) enQueue(D(n1,g,b,s)) 弟弟妹妹进队 } } } 采用队列统计继承人算法的演示为: 最后根据继承人的统计结果,遗产分割如下表所示,并且捐赠给慈善机构14,900万美元。继承顺序人数个人份额(万美元)份额合计(万美元)分割余额(万美元)可分遗产(万美元)个人份额上限(万美元)11300,000300,000200,0002340,000120,00080,00012000040000359,60048,00032,000480009600435,70017,10014,9001920064004.3.5顺序队列数据定义 定义数组Q[M],用Q[0]到Q[M-1]来存放队列的结点。设队首指针为Head,队尾指针为Tail,则队首结点为Q[Head],队尾结点为Q[Tail]。定义FULL等于M-1,EMPTY等于-1,BTM等于0。初始时令Head和Tail都等于EMPTY,表示空队。 #define M 1000 #define FULL M-1 /*队尾指针的最大值 */ #define BTM 0 /*队首指针的最小值 */ #define EMPTY BTM-1 /*空队指针值 */ short Q[M]; /*用数组存放队列 */ short Head=EMPTY; /*队首指针,初始为空队 */ short Tail=EMPTY; /*队尾指针,初始为空队 */顺序队列的出队操作下溢出 结点出队时,如果Head和Tail都等于EMPTY,表示需要在空队中取出结点,说明程序设计有误或者运行过程出错,称为下溢出。正常出队 如果Head小于Tail,表示队内有多个结点,可执行正常出队的操作,返回队首结点Q[Head]的值,并令Head加1。程序语句为: key=Q[Head]; /*取结点值 */ if(Head>Tail) /*正常出队 */ Head++; return(key);出空 如果Head等于Tail而不等于EMPTY,表示队内只剩下1个结点,则除了返回队首结点Q[Head]的值,还应该将Head和Tail都置为EMPTY,这种情况称为出空。 key=Q[Head]; /*取结点值 */ if(Head==Tail) /*出空 */ Head=Tail=EMPTY; return(key); 顺序队列的出队函数 shortdeQueue(void) { shortkey; if(Head<BTM||Head>FULL||Tail<BTM||Tail>FULL) error(); /*非法 */ if(Head<=EMPTY||Tail<=EMPTY) error(); /*下溢出 */ key=Q[Head]; if(Head!=Tail) /*正常出队 */ Head++; else /*出空 */ Head=Tail=EMPTY; return(key); }调用方法示例 key=deQueue();顺序队列的进队操作 结点进队时,只要队列中还有存储空间,就应该更新队尾指针Tail(以及队首指针Head),并且将新结点key添加到Q[Tail]。上溢出 如果在满队情况下进队,即Tail等于FULL而且Head等于BTM,表示发生上溢出,说明队列容量不够。程序语句为: if(Tail==FULL&&Head==BTM) error(); /*上溢出 */空队进队 如果Tail和Head都等于EMPTY,表示空队进队,应该令Tail和Head都等于BTM。 if(Tail==EMPTY&&Head==EMPTY) /*空队进队 */ Head=Tail=BTM; /*或者Head++;Tail++; */ Q[Tail]=key;正常进队 如果BTM≤Head≤Tail≤FULL,表示为正常进队情况,令队尾指针Tail加1,将新结点添加到Q[Tail]。程序语句为: if(BTM<=Head&&Head<=Tail&&Tail<=FULL) Q[++Tail]=key; /*正常进队 */ 假溢出 如果Tail等于FULL,而Head大于BTM,说明所有结点靠近队尾,队首方向还有空位,却不能在队尾添加结点,称为假溢出。假溢出的处理办法是把所有结点向队首移动,使空位出现在队尾,然后更新首指针和尾指针,再在队尾添加结点。程序语句为: if(Tail==FULL&&Head>BTM) /*假溢出 */ { for(k=Head;k<=FULL;k++) /*移动队列 */ Q[k-Head]=Q[k]; Tail=Tail-Head+1; /*更新指针 */ Head=BTM; Q[Tail]=key; }顺序队列的进队函数 voidenQueue(shortkey) { shortk; if(Head<EMPTY||Head>FULL||Tail<EMPTY||Tail>FULL) error(); /*非法 */ if(Tail==FULL&&Head==BTM) error(); /*上溢出(overflow) */ if(Tail==FULL&&Head>BTM) /*假溢出 */ { for(k=Head;k<=FULL;k++) /*移动队列 */ Q[k-Head]=Q[k]; Tail=Tail-Head; /*更新指针 */ Head=BTM; } elseif(Head==EMPTY) /*空队进队 */ Head++; Q[++Tail]=key; /*包含正常进队 */ return; }调用方法示例 enQueue(key);顺序队列的问题 观察指针操作: 进队时Tail加1,出队时Head加1。 说明通过进队和出队操作,整个队列将不断向队尾(数组的终端)方向移动。 每当进队时遇到假溢出的情况,就需要执行整个队列向队尾移动的操作。特别地,在满队(Head==BTM&&Tail==FULL)时,交替地出队和进队,就将交替地发生满队出队和假溢出进队,每次都需要整体移动。显然,这种移动是很费时的,也是顺序队列的致命弱点。4.3.6环形队列环形队列 克服顺序队列反复移动的办法是引进环形队列,即在数组Q[M]中将结点Q[0](Q[BTM])看成是结点Q[M-1](Q[FULL])的下一结点。 修改指针的加1操作: 将i++改为i=(i+1)%M,即把Head++改为Head=(Head+1)%M,把Tail++改为Tail=(Tail+1)%M。则当指针i==M-1(FULL)时,(i+1)%M得0(BTM)。环形队列的特点 在环形队列中,Head<Tail(左下图)和Head>Tail(右下图)的两种情况都有可能出现。把Head和Tail的加1操作改为加1后对M取模的操作,形成了数组的首尾相接,从而显示了环形队列的优点。Head<TailHead>Tail环形队列的操作下溢出测试 if(Head==Tail&&Tail==EMPTY) /*与顺序队列相同*/上溢出测试 无论是Head>Tail还是Head<Tail,测试条件相同: if((Tail+1)%M==Head)Head<Tail:Head==BTM,Tail==FULL上溢出:(Tail+1)%M==HeadHead>Tail:Head==Tail+1上溢出:(Tail+1)%M==Head空队设置 Head=Tail=EMPTY; /*与顺序队列相同*/进队时的指针操作 正常(非空队)进队的指针操作: Tail=(Tail+1)%M; 空队时进队的指针操作: Head=Tail=BTM; 或者 Head=BTM; Tail=(Tail+1)%M;出队时的指针操作 正常出队时指针操作: Head=(Head+1)%M; 出空时的指针操作(置空): if(Head==Tail&&Tail>EMPTY) Head=Tail=EMPTY;4.3.7链接队列链接队列的设计 采用始端不含空结点的链表。采用空栈存放暂时不用的结点,使用教材252页的空栈出栈函数popfree()和进栈函数pushfree()。 进队:采用表后插入,即在链表的尾端插入结点 出队:采用表前删除,即在链表的始端删除结点数据定义 #defineNODEstructnode NODE {
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 5《自己的事情自己做》 教学设计-2024-2025学年心理健康(1、2年级)粤教版
- 23月迹(教学设计)-2024-2025学年统编版语文五年级上册
- 九年级化学上册 3.2 溶液组成的定量表示教学设计1 (新版)鲁教版
- 2023六年级英语下册 Unit 3 Who's That Man第1课时教学设计 陕旅版(三起)
- 2023九年级数学上册 第2章 一元二次方程2.1 一元二次方程教学设计 (新版)湘教版
- 18 文言文二则 囊萤夜读(教学设计)-2023-2024学年统编版语文四年级下册
- 清洁安全培训
- Unit 4 school days further study教学设计 -2024-2025学年译林版七年级英语上册
- Unit 5 The colourful world Part A Letters and sounds大单元整体教学设计表格式-2024-2025学年人教PEP版(2024)英语三年级上册
- 《第三单元 欣赏 春江花月夜》教学设计 -2023-2024学年初中音乐人教版七年级下册
- 合同管理知识培训课件
- 2025年-浙江建筑安全员A证考试题库附答案
- 2025届山西省高三一模地理试题(原卷版+解析版)
- 八下历史第三单元大单元教学设计
- 2024年电信销售员工年终总结
- 2025年度执业药师职务聘用协议模板
- Unit3 Weather Part A(说课稿)-2023-2024学年人教PEP版英语四年级下册
- 2025年高考政治一轮复习知识清单必修四《哲学与文化》重难点知识
- 12万吨年丁二烯抽提装置、10-3万吨年MTBE-丁烯-1装置总承包工程施工组织设计
- 可填充颜色的地图(世界、中国、各省份)
- 华岐钢管合格证(共1页)
评论
0/150
提交评论