数据结构简明教程(第3版)课件 第3章栈和队列_第1页
数据结构简明教程(第3版)课件 第3章栈和队列_第2页
数据结构简明教程(第3版)课件 第3章栈和队列_第3页
数据结构简明教程(第3版)课件 第3章栈和队列_第4页
数据结构简明教程(第3版)课件 第3章栈和队列_第5页
已阅读5页,还剩98页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第3章栈和队列3.1栈3.2队列第3章栈和队列1/1033.1栈3.1.1栈的基本概念栈是一种特殊的线性表,其特殊性体现在元素插入和删除运算上,它的插入和删除运算仅限定在表的某一端进行,不能在表中间和另一端进行。栈的插入操作称为进栈(或入栈),删除操作称为出栈(或退栈)。允许进行插入和删除的一端称为栈顶,另一端称为栈底。处于栈顶位置的数据元素称为栈顶元素。不含任何数据元素的栈称为空栈。3.1栈2/103栈的示意图:3.1栈a1

a2

an栈底栈顶进栈出栈3/103栈的基本运算主要包括以下6种:初始化栈InitStack(st)。建立一个空栈st。销毁栈DestroyStack(st)。释放栈st占用的内存空间。进栈Push(st,x)。将元素x插入栈st中,使x成为栈st的栈顶元素。出栈Pop(st,x)。当栈st不空时,将栈顶元素赋给x,并从栈中删除当前栈顶。取栈顶元素GetTop(st)。若栈st不空,取栈顶元素x并返回1;否则返回0。判断栈空StackEmpty(st)。判断栈st是否为空栈。3.1栈4/103

【例3.1】设一个栈的输入序列为a、b、c、d,借助一个栈所得到的输出序列不可能是()。

A.a、b、c、d B.b、d、c、a

C.a、c、d、b D.d、a、b、c3.1栈栈dcbaD5/103

【例3.2】已知一个栈的进栈序列是1,2,3,…,n,其出栈序列是p1,p2,…,pn,若p1=n,则pi的值为()。

A.i

B.n-i

C.n-i+1 D.不确定3.1栈p1

p2

pi

pnn

n-1…

i

1i+pi=n+1C123…

n

n

n-1n-2…1和为n+1p1=n,只有唯一的出栈序列6/1033.1.2栈的顺序存储结构栈的顺序存储结构称为顺序栈。顺序栈通常由一个一维数组data和一个记录栈顶元素位置的变量top组成。习惯上将栈底放在数组下标小的那端,栈顶元素由栈顶指针top所指向。3.1栈7/103顺序栈类型声明如下:#defineMaxSize100

//顺序栈的初始分配空间大小typedefstruct{ElemTypedata[MaxSize];

//保存栈中元素,这里假设ElemType为char类型inttop; //栈顶指针}SqStack;3.1栈8/103顺序栈的几种状态3.1栈-11234top0(a)空栈-11234top0a(b)a进栈-11234top0abc(c)b,c进栈-11234top0ab(d)出栈1次-11234top0(e)出栈2次MaxSize=59/103归纳起来,对于顺序栈st,其初始时置st.top=-1,4要素如下:栈空条件:st.top==-1栈满条件:st.top==MaxSize-1元素x进栈操作:st.top++;将元素x放在st.data[st.top]中出栈元素x操作:取出栈元素x=st.data[st.top];st.top--3.1栈10/103顺序栈的基本运算算法如下。(1)初始化栈运算算法主要操作:设定栈顶指针top为-1。voidInitStack(SqStack&st) //st为引用型参数{

st.top=-1;}3.1栈11/103(2)销毁栈运算算法这里顺序栈的内存空间是由系统自动分配的,在不再需要时由系统自动释放其空间。voidDestroyStack(SqStackst){}3.1栈12/103(3)进栈运算算法主要操作:栈顶指针加1,将进栈元素放进栈顶处。intPush(SqStack&st,ElemTypex){if(st.top==MaxSize-1) //栈满上溢出返回0return0;

else

{st.top++;

st.data[st.top]=x;

return1; //成功进栈返回1

}}3.1栈13/103(4)出栈运算算法主要操作:先将栈顶元素取出,然后将栈顶指针减1。intPop(SqStack&st,ElemType&x){if(st.top==-1) //栈空返回0return0;

else

{x=st.data[st.top];

st.top--;

return1; //成功出栈返回1

}}3.1栈14/103(5)取栈顶元素运算算法

主要操作:将栈指针top处的元素取出赋给变量x。intGetTop(SqStackst,ElemType&x){if(st.top==-1) //栈空返回0return0;

else

{x=st.data[st.top];

return1; //成功取栈顶元素返回1

}}3.1栈15/103(6)判断栈空运算算法主要操作:若栈为空(top==-1)则返回值1,否则返回值0。intStackEmpty(SqStackst){if(st.top==-1)return1; //栈空返回1elsereturn0; //栈不空返回0}3.1栈16/1033.1.3栈的链式存储结构栈的链式存储结构是采用某种链表结构,栈的链式存储结构简称为链栈。这里采用单链表作为链栈,该单链表是不带头结点的。3.1栈anan-1a1∧…ls栈顶栈底17/103链栈的结点类型声明如下:typedefstructnode{ElemTypedata; //存储结点数据,

//这里假设ElemType为char类型

structnode*next;//指针域}LinkStack;3.1栈18/103归纳起来,链栈ls初始时ls=NULL,4要素如下:栈空条件:ls==NULL栈满条件:不考虑元素x进栈操作:创建存放元素x的结点p,将其插入到栈顶位置上出栈元素x操作:置x为栈顶结点的data域,并删除该结点3.1栈19/103链栈的基本运算算法如下。(1)初始化栈运算算法

主要操作:用ls=NULL标识栈为空栈。voidInitStack(LinkStack*&ls){

ls=NULL;}3.1栈20/103(2)销毁栈运算算法链栈的所有结点空间都是通过malloc函数分配的,在不再需要时需通过free函数释放所有结点的空间。voidDestroyStack(LinkStack*&ls){LinkStack*pre=ls,*p;

if(pre==NULL)return; //考虑空栈的情况

p=pre->next;

while(p!=NULL)

{

free(pre); //释放pre结点

pre=p;p=p->next; //pre、p同步后移

}

free(pre); //释放尾结点}3.1栈21/103(3)进栈运算算法

主要操作:先创建一个新结点,其data域值为x;然后将该结点插入到ls结点之后作为栈顶结点。intPush(LinkStack*&ls,ElemTypex) {LinkStack*p;

p=(LinkStack*)malloc(sizeof(LinkStack));

p->data=x; //创建结点p用于存放x

p->next=ls; //插入p结点作为栈顶结点

ls=p;return1;}3.1栈22/103(4)出栈运算算法

主要操作:将栈顶结点(即ls->next所指结点)的data域值赋给x,然后删除该栈顶结点。intPop(LinkStack*&ls,ElemType&x) {LinkStack*p;

if(ls==NULL) //栈空,下溢出返回0return0;

else

//栈不空时出栈元素x并返回1

{ p=ls; //p指向栈顶结点

x=p->data; //取栈顶元素x ls=p->next; //删除结点p free(p); //释放p结点

return1;

}}3.1栈23/103(5)取栈顶元素运算算法

主要操作:将栈顶结点(即ls->next所指结点)的data域值赋给x。intGetTop(LinkStack*ls,ElemType&x){if(ls==NULL) //栈空,下溢出时返回0

return0;

else

//栈不空,取栈顶元素x并返回1

{x=ls->data;

return1;

}}3.1栈24/103(6)判断栈空运算算法

主要操作:若栈为空(即ls->next==NULL)则返回值1,否则返回值0。intStackEmpty(LinkStack*ls){if(ls==NULL)return1;

elsereturn0;}3.1栈25/103

【例3.5】以下各链表均不带有头结点,其中最不适合用作链栈的链表是()。A.只有表尾指针没有表头指针的循环单链表B.只有表头指针没有表尾指针的循环单链表C.只有表头指针没有表尾指针的循环双链表D.只有表尾指针没有表头指针的循环双链表3.1栈26/103anan-1a1…first栈底栈顶B.只有表头指针没有表尾指针的循环单链表进栈操作:q=first;while(q->next!=first)q=q->next; //查找尾结点qp=(LinkStack*)malloc(sizeof(LinkStack));p->data=x;p->next=first;first=p;q->next=first; //改为循环单链表时间复杂度为O(n)3.1栈27/103anan-1a1…first栈底栈顶B.只有表头指针没有表尾指针的循环单链表出栈操作:q=first;while(q->next!=first)q=q->next; //查找尾结点qp=first;x=p->data;first=first->next;q->next=first; //改为循环单链表free(p);时间复杂度为O(n)3.1栈28/1033.1.4栈的应用示例在较复杂的数据处理过程中,通常需要保存多个临时产生的数据。如果先产生的数据后进行处理,那么需要用栈来保存这些数据。后面示例算法中均采用顺序栈,实际上采用链栈完全相同。3.1栈29/103

【例3.6】假设以I和O分别表示进栈和出栈操作,栈的初态和终态均为空,进栈和出栈的操作序列可表示为仅由I和O组成的序列。①下面所示的序列中哪些是合法的?

A.IOIIOIOO B.IOOIOIIO

C.IIIOIOIO D.IIIOOIOO②通过对①的分析,设计一个算法利用链栈的基本运算判定操作序列str是否合法。若合法返回1;否则返回0。3.1栈30/103①A、D均合法,而B、C不合法。3.1栈A.IOIIOIOO B.IOOIOIIO C.IIIOIOIO D.IIIOOIOO31/103②用一个顺序栈st来判断操作序列是否合法。#include"Sqstack.cpp"//包含前面的顺序栈基本运算函数intJudge(charstr[],intn){inti;ElemTypex;SqStackst; //定义一个顺序栈stInitStack(st); //栈初始化3.1栈32/103for(i=0;i<n;i++) //遍历str的所有字符{if(str[i]=='I') //为'I'时进栈Push(st,str[i]);elseif(str[i]=='O') //为'O'时出栈{if(!Pop(st,x)) //若栈下溢出,则返回0{DestroyStack(st);return0;}}}if(StackEmpty(st)) //栈为空时返回1;否则返回0{DestroyStack(st);return1;}else{DestroyStack(st);return0;}}3.1栈33/103

【例3.7】回文指的是一个字符串从前面读和从后面读都一样,如"abcba"、"123454321"都是回文。

设计一个算法利用顺序栈的基本运算判断一个字符串是否为回文。3.1栈34/103#include"SqStack.cpp" //包含前面的顺序栈基本运算函数intPalindrome(charstr[],intn){SqStackst; //定义一个顺序栈st

InitStack(st); //栈初始化inti;charch;3.1栈35/103for(i=0;i<n;i++) //所有字符依次进栈

Push(st,str[i]);i=0; //从头开始遍历strwhile(!StackEmpty(st)) //栈不空循环{Pop(st,ch); //出栈元素chif(ch!=str[i++]) //两字符不相同时返回0{DestroyStack(st);return0;}}

DestroyStack(st); //销毁栈streturn1; //所有相应字符都相同时返回1}3.1栈36/103

【例3.8】设计一个算法,判断一个可能含有小括号('('与')'、)、中括号('['与']')和大括号('{'与'}')的表达式中各类括号是否匹配。若匹配,则返回1;否则返回0。3.1栈37/103设置一个顺序栈st,定义一个整型flag变量(初始为1)。用i扫描表达式exp,当i<n并且flag=1时循环:当遇到左括号“(”、“[”、“{”时,将其进栈;遇到“}”、“]”、“)”时,出栈字符ch,若出栈失败(下溢出)或者ch不匹配,则置flag=0退出循环;否则直到exp扫描完毕为止。若栈空并且flag为1则返回1,否则返回0。3.1栈设计思路:38/103#include"SqStack.cpp" //包含前面的顺序栈基本运算函数intMatch(charexp[],intn) //exp存放表达式{SqStackst; //定义一个顺序栈st

InitStack(st); //栈初始化intflag=1,i=0;charch;3.1栈39/103while(i<n&&flag==1) //遍历表达式exp{switch(exp[i]){

case'(':case'[':case'{':

//各种左括号进栈

Push(st,exp[i]);break;

case')':

//判断栈顶是否为'('if(!Pop(st,ch)||ch!='(') //出栈操作失败或不匹配flag=0;break;

case']':

//判断栈顶是否为'['if(!Pop(st,ch)||ch!='[') //出栈操作失败或不匹配flag=0;break;

case'}':

//判断栈顶是否为'{'if(!Pop(st,ch)||ch!='{') //出栈操作失败或不匹配flag=0;break;}i++;}3.1栈40/103if(StackEmpty(st)&&flag==1)//栈空且符号匹配则返回1{DestroyStack(st); //销毁栈streturn1;}else{DestroyStack(st); //销毁栈streturn0; //否则返回0}}3.1栈41/1033.2队列3.2.1队列的基本概念队列(简称队)也是一种运算受限的线性表,在这种线性表上,插入限定在表的某一端进行,删除限定在表的另一端进行。队列的插入操作称为进队,删除操作称为出队。允许插入的一端称为队尾,允许删除的一端称为队头。新插入的元素只能添加到队尾,被删除的只能是排在队头的元素。3.2队列42/103归纳起来,一个队列的示意图。a1

a2

an队头队尾进队出队3.2队列43/103队列的基本运算如下:初始化队列InitQueue(qu)。建立一个空队qu。销毁队DestroyQueue(qu)。释放队列qu占用的内存空间。进队EnQueue(qu,x)。将x插入到队列qu的队尾。出队DeQueue(qu,x)。将队列qu的队头元素出队并赋给x。取队头元素GetHead(qu,x)。取出队列qu的队头元素并赋给x,但该元素不出队。判断队空QueueEmpty(qu)。判断队列qu是否为空。3.2队列44/103【例3.10】以下属于队列的基本运算的是()。A.对队列中的元素排序

B.取出最近进队的元素C.在队列中某元素之前插入元素

D.删除队头元素3.2队列45/1033.2.2队列的顺序存储结构队列通常有两种存储结构,即顺序存储结构和链式存储结构。队列的顺序存储结构简称为顺序队,它由一个一维数组(用于存储队列中元素)及两个分别指示队头和队尾的变量组成,这两个变量分别称为“队头指针”和“队尾指针”。通常约定队尾指针指示队尾元素的当前位置,队头指针指示队头元素的前一个位置。3.2队列46/103顺序队的类型声明如下:#defineMaxSize20 //指定队列的容量typedefstruct{ElemTypedata[MaxSize]; //保存队中元素

//这里假设ElemType为char类型

intfront,rear; //队头和队尾指针}SqQueue;3.2队列47/103顺序队的几种状态-11234rear0(a)空队front-11234rear0(b)a进队fronta-11234rear0(c)b,c,d,e进队frontabcde-11234rear0(d)出队2次frontcde-11234rear0(e)出队3次front3.2队列48/103

从中可以看到,图(a)和(e)都是队空的情况,均满足front==rear的条件,所以可以将front==rear作为队空的条件。那么队满的条件如何设置呢?受顺序栈的启发,似乎很容易得到队满的条件为rear==MaxSize-1。?-11234rear0(a)空队front-11234rear0(e)出队3次front3.2队列49/103显然这里有问题,因为图(d)和(e)都满足这个“队满”的条件,而实际上队列并没有满。这种因为队满条件设置不合理而导致的“溢出”称为假溢出,也就是说这种“溢出”并不是真正的溢出,尽管队满条件成立了,但队列中还有多个存放元素的空位置。-11234rear0(d)出队2次frontcde-11234rear0(e)出队3次front3.2队列50/103为了能够充分地使用数组中的存储空间,可以把数组的前端和后端连接起来,形成一个环形的表,即把存储队列元素的表从逻辑上看成一个环。这个环形的表叫做循环队列或环形队列。3.2队列51/103-11234rear0frontcde01234cdefrontrear前端和后端连接起来,形成一个环形的表3.2队列52/103队头队尾指针进1的操作为:队头指针进1:front=(front+1)MODMaxSize队尾指针进1:rear=(rear+1)MODMaxSize3.2队列53/10301234frontrear(a)空队01234rearfront(b)a进队a01234frontrear(c)b,c,d,e进队abcde01234(e)出队3次frontrear3.2队列01234frontrear(d)出队2次cde54/103在循环队列中仍不能区分队空和队满,图(a)、(c)和(e)都满足条件front==rear。01234frontrear(a)空队01234frontrear(c)b,c,d,e进队abcde01234(e)出队3次frontrear3.2队列55/103仍设置队空条件为front==rear。将队满条件设置为(rear+1)MODMaxSize==front。也就是说,当rear指到front的前一位置时就认为队列满了。规定:3.2队列试探进队,若达到队头指针位置,则认为队满!56/103显然在这样设置的队满条件下,队满条件成立时队中还有一个空闲单元,也就是说这样的队中最多只能进队MaxSize-1个元素。01234frontrear(c)b,c,d,e进队abcdeMaxSize=5,最多只有4个元素在循环队列中,不会发生这样的情况3.2队列57/103归纳起来,上述设置的循环队列sq的4要素如下:队空条件:sq.front==sq.rear队满条件:(sq.rear+1)%MaxSize==sq.front进队操作:sq.rear=(sq.rear+1)%MaxSize; sq.data[sq.rear]=x;出队操作:sq.front=(sq.front+1)%MaxSize; x=sq.data[sq.front];3.2队列58/103循环队列的基本运算算法如下。(1)初始化队列运算算法主要操作:指定sq.front=sq.rear=0。voidInitQueue(SqQueue&sq) //sq为引用型参数{

sq.rear=sq.front=0; //队列指针初始化}3.2队列59/103(2)销毁队列运算算法这里顺序队的内存空间是由系统自动分配的,在不再需要时由系统自动释放其空间。voidDestroyQueue(SqQueuesq){}3.2队列60/103(3)进队运算算法

主要操作:先判断队列是否已满,若不满,让队尾指针循环进1,在该位置存放x。intEnQueue(SqQueue&sq,ElemTypex){if((sq.rear+1)%MaxSize==sq.front)//队满上溢出return0;sq.rear=(sq.rear+1)%MaxSize;

//队尾循环进1

sq.data[sq.rear]=x;

return1;}3.2队列61/103(4)出队运算算法

主要操作:先判断队列是否已空,若不空,让队头指针循环进1,将该位置的元素值赋给x。intDeQueue(SqQueue&sq,ElemType&x){

if(sq.rear==sq.front) //队空下溢出

return0;

sq.front=(sq.front+1)%MaxSize; //队头循环进1

x=sq.data[sq.front];

return1;}3.2队列62/103(5)取队头元素运算算法

主要操作:先判断队列是否已空,若不空,将队头指针前一个位置的元素值赋给x。intGetHead(SqQueuesq,ElemType&x){if(sq.rear==sq.front) //队空下溢出return0;

x=sq.data[(sq.front+1)%MaxSize];

return1;}3.2队列63/103(6)判断队空运算算法主要操作:若队列为空,则返回1;否则返回0。intQueueEmpty(SqQueuesq){if(sq.rear==sq.front)return1;

elsereturn0;}3.2队列64/103

【例3.12】若用一个大小为6的数组来实现循环队列,队头指针front指向队列中队头元素的前一个位置,队尾指针rear指向队尾元素的位置。若当前rear和front的值分别为0和3,当从队列中删除一个元素,再加入两个元素后,rear和front的值分别为()。

A.1和5

B.2和4

C.4和2

D.5和1Brear=0,front=3,MaxSize=6front=(front+1)%MaxSize=4rear=(rear+2)%MaxSize=23.2队列65/103

【例3.13】对于循环队列,写出求队列中元素个数的公式,并编写相应的算法。

3.2队列队中元素个数=(rear-front+MaxSize)%MaxSize循环队列元素个数的计算公式如下:66/103对应的算法如下:intCount(SqQueuesq){

return(sq.rear-sq.front+MaxSize)%MaxSize;}3.2队列67/103

【例3.15】如果用一个大小为MaxSize的环形数组表示队列,该队列只有一个队头指针front,不设队尾指针rear,而改置一个计数器count,用以记录队列中的元素个数。

(1)队列中最多能容纳多少个元素?

(2)设计实现队列基本运算的算法。3.2队列68/103设计队列的类型如下:typedefstruct{ElemTypedata[MaxSize]; //存放队列中的元素

intfront; //队头指针

intcount; //队列中元素个数}SQueue;3.2队列69/103(1)队列中最多可容纳MaxSize个元素,因为这里不需要空出一个位置以区分队列空和队列满的情况。(2)队列sq为空的条件是:sq.count==0;队列为满的条件是:sq.count==MaxSize;队尾元素的位置是:(sq.front+sq.count)%MaxSize。3.2队列70/103//----队初始化算法----voidInitQueue(SQueue&qu){qu.front=qu.count=0;}//----销毁队列算法----voidDestroyQueue(SQueuesq){}3.2队列71/103//----元素进队算法----intEnQueue(SQueue&sq,ElemTypex){if(sq.count==MaxSize)return0; //队满

sq.count++;

//队中元素个数增1

sq.data[(sq.front+sq.count)%MaxSize]=x;

return1;}3.2队列72/103//----出队元素算法----intDeQueue(SQueue&sq,ElemType&x){if(sq.count==0)return0;

//队空

sq.count--;

//队中元素个数减1

sq.front=(sq.front+1)%MaxSize;

x=sq.data[sq.front];

return1;}3.2队列73/103//----取队头元素算法----intGetHead(SQueuesq,ElemType&x){if(sq.count==0)return0;

//队空

x=sq.data[(sq.front+1)%MaxSize];

return1;}//----判队空算法----intQueueEmpty(SQueuesq){if(sq.count==0)return1;

//队空返回1

elsereturn0;

//队不空返回0}3.2队列74/1033.2.3队列的链式存储结构队列的链式存储结构简称为链队。这里采用的链队是一个同时带有队头指针front和队尾指针rear的单链表。队头指针指向队头结点,队尾指针指向队尾结点即单链表的尾结点,并将队头和队尾指针结合起来构成链队结点。3.2队列75/103使用的链队的基本结构:a1a2an∧frontrear…lq链队结点队头队尾数据结点typedefstructQNode{ElemTypedata;

//存放队中元素

structQNode*next;//指向下一个结点}QType; //数据结点类型typedefstructqptr{QType

*front;

//队头指针

QType

*rear;

//队尾指针}LinkQueue;

//链队结点类型3.2队列76/103归纳起来,链队lq的4要素如下:队空条件:lq->front==NULL队满条件:不考虑(因为每个结点是动态分配的)进队操作:创建结点p,将其插入到队尾,并由lq->rear指向它出队操作:删除队头的结点a1a2an∧frontrear…lq链队结点队头队尾数据结点3.2队列77/103在链队上实现队列基本运算算法如下。(1)初始化队列运算算法

主要操作:创建链队结点,并置该结点的rear和front均为NULL。voidInitQueue(LinkQueue*&lq){lq=(LinkQueue*)malloc(sizeof(LinkQueue));

lq->rear=lq->front=NULL; //初始时队头和队尾指针均为空}3.2队列78/103(2)销毁队列运算算法先像释放单链表一样释放队中所有数据结点。然后释放链队结点lq。3.2队列79/103voidDestroyQueue(LinkQueue*&lq){QType*pre=lq->front,*p;

if(pre!=NULL) //非空队的情况

{if(pre==lq->rear) //只有一个数据结点的情况free(pre); //释放pre结点else

//有两个或多个数据结点的情况{p=pre->next;while(p!=NULL){free(pre);

//释放pre结点

pre=p;p=p->next;//pre、p同步后移

}

free(pre); //释放尾结点

}

free(lq); //释放链队结点

}}3.2队列80/103(3)进队运算算法

主要操作:创建一个新结点,将其链接到链队的末尾,并由rear指向它。intEnQueue(LinkQueue*&lq,ElemTypex){QType*s;s=(QType*)malloc(sizeof(QType)); //创建新结点s

s->data=x;s->next=NULL;

if(lq->front==NULL)

//原队为空队的情况lq->rear=lq->front=s; //front和rear均指向s结点

else

//原队不为空队的情况

{lq->rear->next=s;

//将s链到队尾

lq->rear=s; //rear指向它

}return1;}3.2队列81/103(4)出队运算算法

主要操作:将front结点的data域值赋给x,并删除该结点。intDeQueue(LinkQueue*&lq,ElemType&x){QType*p;

if(lq->front==NULL) //原队为空队的情况return0;p=lq->front; //p指向队头结点

x=p->data; //取队头元素值

if(lq->rear==lq->front)//原队只有一个结点,删除后队变空lq->rear=lq->front=NULL;

else

//原队有两个或以上结点的情况lq->front=lq->front->next;

free(p);

return1;}3.2队列82/103(5)取队头元素运算算法主要操作:将front结点的data域值赋给x。intGetHead(LinkQueue*lq,ElemType&x){if(lq->front==NULL)

//原队为队空的情况return0;x=lq->front->data;

return1;}3.2队列83/103(6)判断队空运算算法主要操作:若链队为空,则返回1;否则返回0。intQueueEmpty(LinkQueue*lq){if(lq->front==NULL)return1; //队空返回1elsereturn0; //队不空返回0}3.2队列84/103

【例3.16】若使用不带头结点的循环链表来表示队列,lq是这样的链表中尾结点指针。试基于此结构给出队列的相关运算算法。a1a2an…lq队头队尾由lq唯一标识链表(链队)3.2队列85/103这里使用的循环链表不带头结点。a1a2an…lq队头队尾typedefstructnode{ElemTypedata;

//数据域

structnode*next;

//指针域}QNode;

//链队数据结点类型lq始终指向队尾结点,lq->next即为队头结点。当lq==NULL时队列为空。lq->next==lq表示队列中只有一个结点。3.2队列86/103//---初始化队列运算算法---voidInitQueue(QNode*&lq){lq=NULL;}a1a2an…lq队头队尾3.2队列87/103//----销毁链队----voidDestroyQueue(QNode*&lq){QNode*pre,*p;

if(lq!=NULL)

{if(lq->next==lq) //原队中只有一个结点

free(lq);

else

//原队中有两个或以上的结点

{pre=lq;

p=pre->next;

while(p!=lq)

{free(pre);

pre=p;

p=p->next; //pre和p同步后移

}

free(pre);

//释放最后一个结点}

}}a1a2an…lq队头队尾3.2队列88/103//----进队运算算法----voidEnQueue(QNode*&lq,ElemTypex){QNode*s;

s=(QNode*)malloc(sizeof(QNode));

s->data=x; //创建存放x的结点s

if(lq==NULL) //原为空队

{lq=s;

lq->next=lq; //构成循环单链表

}

else

//原队不空,结点s插到队尾

{s->next=lq->next;

lq->next=s;lq=s; //lq指向结点s

}}a1a2an…lq队头队尾3.2队列89/103//-----出队运算算法-----intDeQueue(QNode*&lq,ElemType&x){QNode*s;

if(lq==NULL)return0; //原为队空

if(lq->next==lq)

//原队只有一个结点

{x=lq->data;

free(lq);

lq=NULL;

}

else

//原队有两个或以上的结点

{ s=lq->next;

//将lq之后的结点s删除

x=s->data; lq->next=s->next; free(s);

}

return1;}a1a2an…lq队头队尾3.2队列90/103//----取队头元素运算算法----intGetHead(QNode*lq,ElemType&x){if(lq==NULL)return0; //原为队空

x=lq->next->data;

return1;}//----判断队空运算算法----intQueueEmpty(QNode*lq){if(lq==NULL)return1;

//队空返回1

elsereturn0; //队不空返回0}a1a2an…lq队头队尾3.2队列91/1033.2.4队列的应用示例

【例3.18】设计一个程序,反映病人到医院看病、排队看医生的过程。

3.2队列92/103病人排队看医生采用先到先看的方式,所以采用一个队列模拟。由于病人人数具有较大的不确定性,这里采用一个带头结点的单链表作为队列的存储结构。为了简单,病人通过其姓名来唯一标识。例如,有Smith、John和Mary三个病人依次排队的病人队列:SmithJohnMary∧frontrearlq链队结点队头队尾数据结点3.2队列93/103完整的程序如下:#include<stdio.h>#include<malloc.h>#include<string.h>typedefstructLnode{chardata[10]; //存放患者姓名

structLnode*next; //指针域}QType; //链队结点类型typedefstruct{QType

*front; //指向队头病人结点

QType

*rear; //指向队尾病人结点}LQueue; //病人链队类型SmithJohnMary∧frontrearlq链队结点队头队尾数据结点3.2队列94/103//---初始化队列运算算法---voidInitQueue(LQueue*&lq){lq=(LQueue*)malloc(sizeof(LQueue));lq->rear=lq->front=NULL;

//初始时队头和队尾指针均为空}SmithJohnMary∧frontrearlq链队结点队头队尾数据结点3.2队列95/103voidDestroyQueue(LQueue*&lq) //----销毁链队----{QType*pre=lq->front,*p;

if(pre!=NULL) //非空队的情况

{if(pre==lq->rear) //只有一个数据结点

free(pre); //释放pre结点

else

//有2个或多个数据结点

{p=pre->next;

while(p!=NULL)

{free(pre); //释放pre结点

pre=p;p=p->next; //pre、p同步后移

}

free(pre); //释放尾结点

}}free(lq); //释放链队结点}3.2队列96/103//----进队运算算法----voidEnQueue(LQueue*&lq,charx[]){QType*s;

s=(QType*)malloc(sizeof(QType)); //创建新结点sstrcpy(s->data,x);s->next=NULL;

if(lq->front==NULL) //原队为空队的情况

lq->rear=lq->front=s; //front和rear均指向s结点

else

//原队不为空队的情况

{lq->rear->next=s; //将s链到队尾

lq->rear=s; //rear指向它

}}SmithJohnMary∧frontrearlq链队结点队头队尾数据结点3.2队列97/103//-----出队运算算法-----intDeQueue(LQueue*&lq,charx[]){

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论