第三章栈和队列Word文档格式.docx
- 文档编号:16386160
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:30
- 大小:48.32KB
第三章栈和队列Word文档格式.docx
《第三章栈和队列Word文档格式.docx》由会员分享,可在线阅读,更多相关《第三章栈和队列Word文档格式.docx(30页珍藏版)》请在冰豆网上搜索。
②栈空的判定条件:
top==-1;
栈满的判定条件:
S-top==Stacksize-1;
③栈中的元素个数:
top+1;
④若S是结构型变量则所涉及操作中的S->
应改为S.;
3.栈的基本运算在顺序栈上的实现
(1)进栈操作(push(Seqstack*S,datatypex))
基本步骤:
若栈不满,则
栈顶指针加1:
top++;
元素入栈:
data[S-top]=x;
(2)出栈操作(pop(Seqstack*S))
若栈非空,则
读取栈顶元素:
x=S->
栈顶指针减1:
top--;
返回栈顶元素:
return(x);
【分析】出栈操作和入栈操作是栈的两种重要操作,应注意操作的判定条件。
栈的有效使用空间是0到Stacksize-1,所以,当S->
top==-1时,表示栈空;
当S->
top==Stacksize-1时,表示栈满。
时间复杂度均为O
(1)。
3.1.3链栈
1.链栈及类型定义
栈以链式存储方式存储所形成的存储结构,称为链栈。
就是单链表。
其数据类型定义如下:
typedefstructstacknode{
datatypedata;
structstacknode*next;
}Stacknode;
typedefstruct{
Stacknode*stop;
}Linkstack;
Linkstack*s;
2.链栈操作应注意的问题
与链表不同,链栈的插入和删除操作仅限定在表头位置进行,所以,链栈不需要附加头结点。
链表的头指针就是链栈的栈顶指针:
top;
栈顶元素为:
top->
data。
空栈的判定条件是:
top==null.;
只要系统的备用空间能够分配出存储结点就不会发生上溢。
所以,程序员不必考虑链栈栈满的问题。
链栈的操作就是单链表的操作,只需注意栈本身的操作要求即可。
如:
进栈、出栈操作就是在表头进行插入和删除;
求栈中的元素个数就是求链表中的结点个数。
3.1.4栈的应用举例
1.设计算法判断一个算术表达式的圆括号是否正确配对。
(提示:
凡遇‘(‘就进栈,遇‘)’就退掉栈顶的‘(‘,表达式扫描完毕,栈应为空)
算法
intbracketsmatch(chara[])
{//设算术表达式以字符串形式存储在数组中
Seqstack*S;
Initstack(S);
intk=0;
while(a[k]!
=’\0’)
if(a[k]=='(')Push(S,'(');
elseif(a[k]==')')
if(Stackempty(S))return0;
elsePop(S);
if(Stackempty(S))return1;
elsereturn0;
}
2.一个双向栈S是在同一向量空间里实现的两个栈,它们的栈底分别设在向量空间的两端。
试为此双向栈设计初始化Initstack(S)、入栈push(S,x,i)和出栈pop(i)算法,其中,i为0或1用于指示栈号。
#definmaxsize100
typedefstructnode{
datatypedata[maxsize];
inttop1,top2;
}Seqstack;
(1)双向栈的初始化
viodInitstack(Seqstack*S)
{
S->
top1=-1;
top2=maxsize;
(2)双向栈入栈算法
viodpush(Seqstack*S,datatypex,inti)
{if(S->
top1+1==S->
top2)
erroe(overflow);
else
if(i==0)
{S->
top1++;
top1]=x;
top2--;
top2]=x;
}
(3)双向栈出栈算法
datatypepop(Seqstack*S,inti)
{if(i==0)
if(S->
top1==-1)
error(downflow);
{returnS->
top1]}
if(S->
top2==maxsize)
error(downflow);
else
returnS->
top2];
3.Ackerman函数的定义如下:
n+1当m=0时
AKM(m,n)=AKM(m-1,1)当m≠0,n=0时
AKM(m-1,AKM(m,n-1)) 当m≠0,n≠0时
请写出递归算法。
算法如下:
intakm(intn,intm)
{if(m==0)
returnn+1;
elseif(n==0&
&
m!
=0)
returnakm(m-1,1);
returnakm(m-1,akm(m,n-1));
3.2队列
3.2.1队列的定义及其基本运算
1.队列的概念
队列:
是一种操作受限的线性表。
它只允许在表的一端进行插入,在表的另一端进行删除。
允许删除的一端称为的队头(front),允许插入的一端称为队尾(rear)。
当队列中没有元素时称为空队列。
队列中包含的元素数称为队列的长度。
空队列的长度为0。
2.队列的特点:
从定义不难看出队列具有先进先出(firstinfirstout)的特点,简称为FIFO表。
3.队列的基本运算:
Initqueue(Q):
置空队。
构造一个供空队列;
Queueempty(Q):
判定队列Q是否为空,为空返回真值,否则返回价假值;
Queuefull(Q):
判定队列Q是否已满,若已满返回真值,否则返回价假值;
Enqueue(Q,x):
入对操作。
如果队列不满,则将元素x插入队列Q;
Dequeue(Q):
出队操作。
若队列非空,则删除队头元素并将其返回;
Queuefront(Q):
读取队头元素。
若队列非空,则返回队头元素。
3.2.2顺序队列
1.顺序队列与循环队列
队列的顺序存储结构称为顺序队列,顺序队列实际上是运算受限的顺序表。
由于队列的操作总是在表的两端进行,所以设置两个指针分别指向对头和队尾位置。
顺序队列存在的“假溢出”现象,使得被删除元素的存储空间永远无法被重新利用,所以顺序队列不能充分利用存储空间。
为解决假溢出现象,我们可以将向量空间想象为一个首尾相接的环形结构,并称这种向量为循环向量,存储在其中的队列称为循环队列。
通常,我们总是在循环队列上完成队列的操作。
2.循环队列的类型定义:
#defineQueuesize100
datatypedata[Queuesize];
intfront,rear;
}Cirqueue;
这里我们将循环队列的存储结构定义为结构类型Cirqueue。
应用时,可根据具体需要将队列定义为结构体变量或指向结构的指针变量。
例如:
Cirqueue*Q,Q1;
其中,Q为指针变量,Q1为结构体变量注意二者访问成员的方法不同。
3.循环队列应注意的几个问题
①队头指针和队尾指针在循环意义下加1操作的实现:
(向量下标的有效范围下界是0,上界是Queuesize-1)
Q->
front=(Q->
front+1)%Queuesize;
rear=(Q->
rear+1)%Queuesize;
②循环队列队满、队空的判定:
为了区别队满和队空,有三种方法可供采用,分别为:
方法1:
引入一个布尔变量以区别队列的空和满两种状态;
方法2:
采用少用一个元素的空间的策略,例如:
队头指针指向队头元素,队尾指针指向队尾元素下一个位置,则
队空的判定条件为:
Q-front==Q->
rear
队满的判定条件为:
Q->
front==(Q->
rear+1)%Queuesize;
但方法3:
引入一个计数器记录队列元素的总数。
在众多的参考书中更多地采用的方法2,应对方法2给予足够的重视。
4.基本运算在循环队列上的实现(队满、队空的判定采用方法2)
(1)入队操作
voidEnqueue(Cirqueue*Q,datatypex){
if(Q->
rear+1)%Queuesize)
error("Queueoverflow");
{Q->
data[Q->
rear]=x;
}
(2)出队操作
datatypeDequeue(Cirqueue*Q){
front==Q->
rear)
error("Queuedownflow");
{x=Q->
front];
return(x);
【分析】入队、出队操作是队列的两种重要操作。
算法采用方法2区别队满和队空,应注意队满和队空的判定条件,以及入队和出队后队尾指针和队头指针在循环队列中的修改(即加1后进行取模运算)。
两算法的时间复杂度均为O
(1)。
3.2.3链队列
1.链队队列及类型定义
队列的链式存储结构简称为链队列。
其是限制仅在表头删除和表尾插入的单链表。
为了操作方便,一个链队列即设置头指针又设置尾指针。
链队列类型描述如下:
typedefstructqueuenode{
structqueuenode*next;
}Queuenode;
//链队列中的结点结构
Queuenode*front;
//队头指针
Queuenode*rear;
//队尾指针
}Linkqueue;
//将队头指针、队尾指针封装在一个结构类型之中
2.链队列操作应注意的几个问题
①链队列可以定义为指针类型,也可以定义为结构类型。
Linkqueue*Q,Q1;
其中,Q是指针型,Q1是结构类型。
应注意二者使用时的区别,作为函数参数,若为加工型操作,则选择指针型,作为引用型操作,则选择结构型。
②与单链表一样为了操作方便,链队列通常也带头结点。
教材中的上算法没有考虑头结点。
③链队列为空的判定条件:
当链队列不带头结点时为:
front==NULL;
当链队列带头结点时为:
rear==Q->
front。
3.队列的基本操作带头结点的链队列上的实现
(1)入队操作
C语言算法:
voidenqueue(Linkqueue*Q,datatypex)
{//将元素x插入队列Q(Q指针型)
Queuenode*p;
p=(Queuenode*)malloc(sizeof(Queuenode));
//动态分配新结点
p->
data=x;
next=null;
//构造新结点
rear->
next=p;
rear=p;
//完成插入操作
C语言算法
datatypeDequeue(Linkqueue*Q)
{//删除队头元素并将其值返回
datatypex;
If(Q->
error("queuedownflow");
//队列若为空,则出错返回
p=Q->
front->
next;
//工作指针p指向队头元素
x=p->
data;
//保存队头元素值
next=p->
//
if(Q->
rear==p)
rear=Q->
front;
//删除队头元素后若队列有空,则修改队尾指针
free(p);
//释放删除结点
//
插入和删除算法的时间复杂度均为O
(1)。
4.应用举例
1.算法阅读题
如图所示,利用同一循环向量空间实现两个队列,其类型Queue2定义如下:
(2003)
DataTypedata[MaxSize];
intfront[2],length[2];
}Queue2;
对于i=0或1,front[i]和length[i]分别为第i个队列的头指针和长度域。
请在空缺处填入合适的内容,实现第i个循环队列的入队操作。
intEnQueue(Queue2*Q,inti,DataTypex)
{//若第i个队列不满,则元素x入队列,并返回1,否则返回0
if(i<
0||i>
1)return0;
if(
(1))
return0;
data[
(2)]=x;
length[(3)]++;
return1;
【分析】利用同一循环向量空间实现两个循环队列,每个队列分别设置头指针和长度域。
以下标i为0或1区分两个队列。
若当前队列为第i个,则另一个可表示为(i+1)%2;
要实现循环队列的循环机制,在入队、出队确定队头、队尾位置时应以向量长度为模取模,即出队时队头指针为:
front[i]=(front[i]+1)%MaxSize入队时队尾位置为:
(Q->
front[i]+Q->
length[i])%MaxSize。
算法功能是第i个队列的入队操作,所以算法中
(1)处为判断队列是否为满队列;
(2)处为第i个队列的队尾位置;
(3)指定修改第i个队列的队长。
【答案】
(1)Q->
front[(i+1)%2]==(Q->
length[i])%MaxSize
(2)(Q->
(3)i
试题分析与习题
1.历届试题分析
一、选择题:
1.由两个栈共享一个向量空间的好处是( )(2001)
A.减少存取时间,降低下溢发生的机率 B.节省存储空间,降低上溢发生的机率
C.减少存取时间,降低上溢发生的机率 D.节省存储空间,降低下溢发生的机率
【分析】由两个栈共享一个向量空间,使得两个栈的剩余空间可共享,从而节省存储空间,降低上溢发生的机率。
【答案】B
2设数组data[m]作为循环队列SQ的存储空间,front为队头指针,rear为队尾指针,则执行出队操作后头指针的值为(2001)
A.front=front+1B.front=(front+1)%(m-1)
C.front=(front-1)%mD.front=(front+1)%m
【分析】该题的考核点是循环队列(同类型的题还可以是入队操作时,队尾指针的变化)。
模长(数组长度)为m,队头指针后移通过加1实现。
所以,正确答案为D。
【答案】D
3..判定“带头结点的链队列为空”的条件是(
)(2003)
A.Q.front==NULL
B.Q.rear==NULL
C.Q.front==Q.rear
D.Q.front!
=Q.rear
【分析】带头结点的链队列为空,即链队列中只有一个头结点,这时头指针和尾指针均指向了头结点。
所以,正确答案为C。
【答案】C
4.若进栈序列为a,b,c,则通过入出栈操作可能得到的a,b,c的不同排列个数为(
)(2002)
A.4
B.5
C.6
D.7
【分析】
若入栈先后次序为a,b,c,则不能得到c,a,b的出栈序列,可得到的序列为:
abc,acb,bac,bca,cba。
二、填空题
5.栈顶的位置是随着 操作而变化(2001)
【分析】该题考核点是栈的概念。
【答案】入栈和出栈。
6.假设以S和X分别表示进栈和退栈操作,则对输入序列a,b,c,d,e进行一系列栈操作SSXSXSSXXX之后,得到的输出序列为________。
(2002)
【分析】该题考核点是栈的特点:
先进后出。
在入栈序列确定的情况下,入栈、出栈操作的交替次序不同,将会得到不同的出栈序列。
【答案】BCEDA
7.在队列中,允许进行插入操作的一端称为____________,允许进行删除操作的一端称为____________。
【分析】该题考核点是队列的概念。
【答案】队尾、队头
8.如图两个栈共享一个向量空间,top1和top分别为指向两个栈顶元素的指针,则“栈满”的判定条件是____________。
【分析】两个栈共享一个向量空间,两个栈底分别处于向量的两端,而栈顶处于中间,随着入栈操作的进行,当两个栈顶指针相邻时为栈满。
【答案】top1+1==top2(或top1==top2-1)
三、算法阅读题
9.如图所示,利用同一循环向量空间实现两个队列,其类型Queue2定义如下:
DataTypedata[MaxSize];
intfront[2],length[2];
if(
(1)
)
data[
(2)
]=x;
length[
(3)
]++;
return1;
10.设栈S=(1,2,3,4,5,6,7),其中7为栈顶元素。
请写出调用algo(&
s)后栈S的状态。
voidalgo(Stack*S)
{
inti=0;
QueueQ;
StackT;
InitQueue(&
Q);
InitStack(&
T);
while(!
StackEmpty(S))
if((i=!
i)!
=0)Push(&
T,Pop(&
S));
elseEnQueue(&
Q,Pop(&
while(!
QueueEmpty(Q))
Push(&
S,DeQueue(&
Q));
StackEmpty(T))
S,Pop(&
T));
【分析】该题是栈和队列的综合应用题,其功能是通过工作栈T和队列Q对栈中内容重新排放。
算法中第一个循环是将栈S中元素依次出栈,并交替入栈T和入队Q(算法中判断条件(i=!
=0当i为0时成立,当i为1时不成立,i值交替为0和1)。
循环结束后T中值为:
(7,5,3,1),Q中值为:
(6,4,2);
第二个循环是将队列Q中的元素依次出队并入栈S,循环结束后S中值为:
第三个循环是将栈T中的元素依次出栈并入栈S,循环结束后S中值为:
(6,4,2,1,3,5,7)。
【答案】算法结束后S中值为:
11.假设两个队列共享一个循环向量空间(参见右下图),其类型Queue2定义如下:
Datatypedata[maxsizi];
intfront[2],rear[2];
}Queue21;
对于i=0或1,front[i]和rear[i]分别为第i个队列的头指针和尾指针。
请对以下算法填空,实现第i个队列的入队操作。
(2001)
intEnqueue(Queue2*Q,inti,datatypex)
{//
if(i<
1) return0;
if(Q->
rear[i]==Q->
front[ ①]retu
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第三 队列