第3章 栈和队列2.docx
- 文档编号:30061102
- 上传时间:2023-08-04
- 格式:DOCX
- 页数:29
- 大小:108.26KB
第3章 栈和队列2.docx
《第3章 栈和队列2.docx》由会员分享,可在线阅读,更多相关《第3章 栈和队列2.docx(29页珍藏版)》请在冰豆网上搜索。
第3章栈和队列2
第3章栈和队列
栈和队列是两种重要的线性结构。
它们的共同特点是:
操作受限(插入元素和删除元素只能在端点处进行)的线性表。
3.1栈
3.1.1抽象数据类型栈的定义
什么是栈(Stack):
栈是限定仅在表尾进行插入或删除操作的线性表。
对栈来说,表尾端有其特殊的含义,称为栈顶(Top),相应地,表头端称为栈底(Bottom)。
不含元素的栈称为空栈。
栈在生活中的例子:
蒸小茏包,先放下去的最后才拿出来。
栈:
是先进后出(FirstInLastOut,FILO)的线性表,也可称为后进先出(LastinFirstOut,LIFO)。
对应的一个名词:
先进先出FirstInFirstOut(FIFO)。
通常栈可用下图来表示:
假设插入元素的序列为:
49,27,36,57,63,89,16,这个过程称为入栈。
其过程为:
将元素放到当前栈顶指向的空间,然后,栈顶移动一个位置。
出栈:
就是将元素从栈顶元素处删除。
其过程为:
先将栈顶向下移动一个位置,再将该元素拷贝出来。
典型的题目:
如果有4个元素:
abcd,依次入栈,其中又陆续出栈,问有哪些合法的出栈顺序?
1)abcd?
可以
2)abdc?
可以
3)cbda?
可以
4)cadb?
不可能
抽象数据类型的定义:
ADTStack{
数据对象:
数据关系:
基本操作:
1)InitStack,初始化栈
2)DestroyStack,销毁栈,栈的空间释放了
3)ClearStack,将一个栈清空,栈的空间还在
4)StackEmpty,判断一个栈是否是空栈,返回True或False
5)StackLength,求栈的长度,也就是求栈中还有几个元素
6)GetTop,返回栈顶元素,但该元素还在栈中
7)Push,元素入栈
8)Pop,栈顶元素出栈
9)遍历:
StackTraverse,从栈底到栈顶元素,依个进行访问。
3.1.2栈的表示和实现
栈有两种表示:
顺序表示(顺序栈)、链式表示(链表栈)。
所谓顺序栈,就是用一个顺序表(数组)来存储栈的元素。
显然,我们需要这些数据:
存储元素的空间的首地址、栈顶指针、栈的空间总大小。
就得到下列结构体:
1、顺序栈的第一种实现:
typedefstruct{
SElemType*base;//存储元素的空间的首地址,可用malloc函数申请空间,将地址赋给它
SElemType*top;//栈顶指针,指的是当前栈顶元素的上面的那个位置,初始时,top指向base
intstacksize;//当前栈的最大空间大小,用来判断栈是否已经满了,从而可以采取必要的措施。
}SqStack;
#defineSTACK_INIT_SIZE100
#defineSTACK_INCREMENT10
SqStackS;//定义一个栈的变量S
1)初始化栈函数
StatusInitSqStack(SqStack&S)
{
S.base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));//申请100个空间
if(!
S.base)exit(-1);//申请空间失败,直接退出程序
S.top=S.base;
S.stacksize=STACK_INIT_SIZE;
returnOK;
}
2)判断一个栈是否是空栈
StatusSqStackEmpty(SqStackS)
{
if(S.top==S.base)
returnTRUE;
else
returnFALSE;
}
3)清空一个栈
StatusClearSqStack(SqStack&S)
{
S.top=S.base;
returnOK;
}
4)销毁一个栈
StatusDestroySqStack(SqStack&S)
{
free(S.base);
S.base=S.top=NULL;
S.stacksize=0;
returnOK;
}
5)求栈的长度
intSqStackLength(SqStackS)
{
returnS.top-S.base;
}
6)入栈
StatusPushSqStack(SqStack&S,SElemTypee)
{
if(S.top-S.base>=S.stacksize)//栈的空间满了
{
S.base=(SElemType*)realloc(S.base,(S.stacksize+10)*sizeof(SElemType));
if(!
S.base)exit(-1);
S.top=S.base+S.stacksize;
S.stacksize+=10;
}
*(S.top)=e;//
S.top++;
returnOK;
}
7)出栈
StatusPopSqStack(SqStack&S,SElemType&e)
{
if(S.top==S.base)//如果栈为空
returnERROR;
S.top--;
e=*(S.top);
returnOK;
}
8)取栈顶元素
StatusGetTopSqStack(SqStackS,SElemType&e)
{
if(S.top==S.base)//如果栈为空
returnERROR;
e=*(S.top-1);
returnOK;
}
9)遍历:
从栈底开始到栈顶依次访问栈中的元素
StatusTraveseSqStack(SqStackS)
{
SElemType*p=S.base;
while(p { printf(*p);//访问当前元素p p++; } returnOK; } 2、顺序栈的第二种实现: typedefstruct{ SElemType*base; inttop;//栈顶上面元素的相对位置,也表示当前栈中有多少个元素 intstacksize;//当前栈的最大空间大小,用来判断栈是否已经满了 }SqStack; 1)初始化栈函数 StatusInitSqStack(SqStack&S) { S.base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));//申请100个空间 if(! S.base)exit(-1);//申请空间失败,直接退出程序 S.top=0; S.stacksize=STACK_INIT_SIZE; returnOK; } 2)判断一个栈是否是空栈 StatusSqStackEmpty(SqStackS) { if(S.top==0) returnTRUE; else returnFALSE; } 3)清空一个栈 StatusClearSqStack(SqStack&S) { S.top=0; returnOK; } 4)销毁一个栈 StatusDestroySqStack(SqStack&S) { free(S.base); S.base=NULL; S.top=0; S.stacksize=0; returnOK; } 5)求栈的长度 intSqStackLength(SqStackS) { returnS.top; } 6)入栈 StatusPushSqStack(SqStack&S,SElemTypee) { if(S.top==S.stacksize)//栈的空间满了 { S.base=(SElemType*)realloc(S.base,(S.stacksize+10)*sizeof(SElemType)); if(! S.base)exit(-1); S.stacksize+=10; } S.base[S.top]=e;//*(S.base+S.top)=e; S.top++; returnOK; } 7)出栈 StatusPopSqStack(SqStack&S,SElemType&e) { if(S.top==0)//如果栈为空 returnERROR; S.top--; e=S.base[S.top];//e=*(S.base+S.top); returnOK; } 8)取栈顶元素 StatusGetTopSqStack(SqStackS,SElemType&e) { if(S.top==0)//如果栈为空 returnERROR; e=S.base[S.top-1]; returnOK; } 9)遍历: 从栈底开始到栈顶依次访问栈中的元素 StatusTraveseSqStack(SqStackS) { for(inti=0;i { printf(S.base[i]); } returnOK; } 3、共享栈的实现 其结构如下: typedefstruct { intspace[100];//共享栈的存储空间,固定为100个元素 inttop1,top2;//分别表示两个栈的top }ShareStack; ShareStackSS; 则各种操作: 1)初始化栈: StatusInitStack_Share(ShareStack&SS) { SS.top1=0; SS.top2=99; } 2)判断一个栈是否是空栈 StatusStackEmpty_Share(ShareStackSS,intn)//判断第n个栈是否为空栈 { if(n==1)//第一个栈 { if(SS.top1==0)returnTRUE; elsereturnFALSE; }else//第二个栈 { if(SS.top2==99)returnTRUE; elsereturnFALSE; } } 3)清空一个栈 StatusClearStack_Share(ShareStack&SS,intn)//清空第n个栈 { if(n==1) { SS.top1=0; }else { SS.top2=99; } returnOK; } 4)销毁一个栈 StatusDestroyStack_Share(ShareStack&SS,intn)//销毁第n个栈 { //此操作无意义 returnOK; } 5)求栈长度 intStackLength_Share(ShareStackSS,intn)//求第n个栈的长度 { if(n==1) { returnSS.top1; }else { return99-SS.top2; } } 5)入栈 StatusPush(ShareStack&SS,intn,SElemTypee)//入栈到第n个栈中 { if(SS.top1>=SS.top2)//栈满 returnERROR; if(n==1) { SS.spase[SS.top1]=e; SS.top1++; }else { SS.space[SS.top2]=e; SS.top2--; } returnOK; } 6)出栈 StatusPop(ShareStack&SS,intn,SElemType&e)//第n个栈出栈 { if(n==1) { if(SS.top1==0)//第一个栈是空栈,错误 returnERROR; SS.top1--; e=SS.space[SS.top1]; }else { if(SS.top2==99)//第二个栈是空栈,错误 returnERROR; SS.top2++; e=SS.space[SS.top2]; } } 课后练习: 写一个程序,实现上述共享栈,并进行测试 4、栈的链式表示与实现 可以使用的链表有: 单链表(最常用)、单循环链表、循环双链表。 对于单链表来说,其插入: 在表头结点前(方便)、表尾后(方便); 删除: 删除首元结束(方便)、表尾结点(麻烦)。 与栈来对应: 栈只在链表的表头一端进行插入(入栈)或删除(出栈)。 实际上,链式栈就是一个链表,是一个只允许在表头进行插入和删除的链表。 链式栈的所有操作: 初始化、判空、清空、销毁、取栈顶元素、入栈、出栈、遍历; typedefintSElemType_3; typedefstructLkStackNode { SElemType_3data; structLkStackNode*next; }LkStackNode,*LkStack; LkStackS; 1)初始化操作: StatusInitLkStack(LkStack&S) { S=(LkStack)malloc(sizeof(LkStackNode)); S->next=NULL; returnOK; } 2)判断一个栈是否是空栈 StatusLkStackEmpty(LkStackS) { if(! S->next)//等价于if(S->next==NULL) returnTRUE; else returnFALSE; } 3)清空一个栈 StatusClearLkStack(LkStack&S) { LkStackp=S->next; while(p! =NULL) { S->next=p->next; free(p); p=S->next; } returnOK; } 4)销毁一个栈 StatusDestroyLkStack(LkStack&S) { ClearLkStack(S); free(S); S=NULL; returnOK; } 5)求栈的长度 intLkStackLength(LkStackS) { LkStackp=S->next; intn=0; while(p! =NULL) { n++; p=p->next; } returnn; } 6)入栈 StatusPushLkStack(LkStack&S,SElemTypee) { LkStackp; p=(LkStack)malloc(sizeof(LkStackNode)); p->data=e; p->next=S->next; S->next=p; returnOK; } 7)出栈 StatusPopLkStack(LkStack&S,SElemType&e) { if(S->next==NULL)//空栈 returnERROR; LkStackp=S->next; S->next=p->next; e=p->data; free(p); returnOK; } 8)取栈顶元素 StatusGetTopLkStack(LkStackS,SElemType&e) { if(S->next==NULL)//空栈 returnERROR; e=S->next->data; returnOK; } 9)遍历栈: 从栈底到栈顶逐个访问 对于单链表来说,遍历操作比较困难。 如果确定会有遍历操作,则应该使用双循环链表。 3.2栈的应用 栈的应用场合: 需要记忆数据,且记忆后使用按照先进后出的顺序进行。 3.2.1数制转换 如将一个数117转换成2进制: voidConversion(intn,intk)//将整数n转化为k进制并输出 { LkStackS; InitStack_Lk(S); while(n! =0) { y=n%k;//y表示余数 Push(S,y);//余数入栈 n=n/k;// } printf("%d转化为%d进制后,结果为: ",n,k); while(! StackEmpty_Lk(S)) { Pop(S,y); printf("%5d",y); } } 3.2.2迷宫求解 游戏中所有的地图,不管看起来有多复杂,其实内容存储特别简单,以坦克大战游戏为例: 有很多的各种地图道具: 草地、墙、金刚石、湖等,真正存储这个地图不会存储这些道具,全部是用一个数字表示,如1表示草地、2表示墙、3表示金刚石、4表示湖、0表示空地等,那么一个场景的地图可用一个二维数组表示: 三维数组的第一维代表第几关,第二维代表该关的第几行,第三维代表该关的第几列: //共13关,每关20行,每行25列 intmap[13][20][25]= { {//第1关地图数据 {0,1,1,1,2,3,4,.......,1}, {1,1,1,2,2,2,3,.......,1}, ..... }, {//第2关地图数据 {...}, {...}, ... } ...//其它关的地图数据 }; 3.2.3表达式求值 如: (4+2)*(3-10)/55*(4+7)/2-6/(3-1) 比较两者的(前面的一个运算符、后面的一个运算符)优先级: 1)如果是数字,则直接入数字栈 2)如果后面的优先级高: 直接将该运算符入栈 3)如果前者的优先级高: 先算出前者,可得出一个中间结果,并将其入栈 4)如果两者相等,脱括号,或者结果已经在数字栈中: 刚才的运算符与现在的运算符相比较表格: 4*3^2/5 现在 前面 + - * / ^ ( ) # + > > < < < < > > - > > < < < < > > * > > > > < < > > / > > > > < < > > ^ > > > > > < > > ( < < < < < < 〓 不可能 ) > > > > > 不可能 > > # < < < < < < 不可能 = 3.2.3课后练习 1、将程序稍加变化,能运行,并能正确处理多位数和小数(处理小数作为选做题); 2、再将程序稍加变化,使之还能处理乘方运算; 3、练习书上的第5—8套试卷; 3.3队列 队列: Queue,是一种先进先出(FirstInFirstOut),注意与栈区别,栈是FILO。 应用场合: 栈一般用于要进行先后顺序颠倒的地方,而队列用于先后顺序一致的地方,如排队。 在计算机中,也有排队,《操作系统》中介绍的作业、进程队列等。 队列的实现: 有链队列(用链表实现)、循环队列(用顺序表实现) 3.3.1链队列 用一个链表来实现。 重要的操作: 队列也是双端受限的线性表,它只允许一端进行插入(队尾),另一端进行删除(队头)。 队列的各种操作函数: typedefstructQNode{ QElemTypedata; structQNode*next; }QNode,*QueuePtr; typedefstruct{ QueuePtrfront; QueuePtrrear; }LinkQueue; LinkQueueQ; 1)初始化一个空队列: StatusInitQueue(LinkQueue&Q) { Q.front=(QueuePtr)malloc(sizeof(QNode)); Q.front->next=NULL; Q.rear=Q.front; returnOK; } 2)判空,判断一个队列是否为空: StatusQueueEmpty(LinkQueueQ) { returnQ.front==Q.rear; } 3)求长度,求一个队列中元素的个数 intQueueLength(LinkQueueQ) { intn=0; QueuePtrp=Q.front; while(p->next! =NULL) { n++; p=p->next; } } 4)清空: StatusClearQueue(LinkQueue&Q) { QueuePtrp=Q.front->next; while(p! =NULL) { Q.front->next=p->next; free(p); p=Q.front->next; } Q.rear=Q.front; returnOK; } 5)销毁队列: StatusDestroyQueue(LinkQueue&Q) { ClearQueue(Q); free(Q.front); Q.front=Q.rear=NULL; returnOK; } 6)取队头元素: QElemTypeGetHead(LinkQueueQ) { if(! QueueEmpty(Q)) returnQ.front->next->data; else returnERROR; } 7)取队尾元素: QElemTypeGetRear(LinkQueueQ) { returnQ.rear->data; } 8)入队: 一个元素入队,实质就是插入一个结点在队尾: StatusEnQueue(LinkQueue&Q,QElemTypee) { QueuePtrp; p=(QueuePtr)malloc(sizeof(QueueNode)); p->data=e; p->next=NUL
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第3章 栈和队列2 队列