《数据结构课件队列.ppt》由会员分享,可在线阅读,更多相关《数据结构课件队列.ppt(30页珍藏版)》请在三一办公上搜索。
1、栈栈的应用队列队列的应用,第三章 栈和队列,3.4 队列3.4.1 抽象数据类型队列的定义 队列(Queue)也是一种运算受限的线性表。它只允许在表的一端进行插入,而在另一端进行删除。允许删除的一端称为队头(front),允许插入的一端称为队尾(rear)。,例如:排队购物。操作系统中的作业排队。先进入队列的成员总是先离开队列。因此队列亦称作先进先出(First In First Out)的线性表,简称FIFO表。,a0 a1 a2 an-1,rear,队头,队尾,front,队 列 的 示 意 图,队列的特点先进先出,说明:第一个入队的元素在队头,最后一个入队的元素在队尾,第一个出队的元素为
2、队头元素,最后一个出队的元素为队尾元素,队列的抽象数据定义见书59,队列的基本运算 队列可定义如下五种基本运算:,1初始化队列 InitQueue(&Q)将队列Q设置成一个空队列。2入队列 EnQueue(&Q,X)将元素X插入到队尾中,也称“进队”,“插入”。3出队列 DeQueue(&Q,&e)将队列Q的队头元素删除,并用e返回其值,也称“退队”、“删除”。4取队头元素 GetHead(Q,&e)得到队列Q的队头元素之值,并用e返回其值。5判队空 QueueEmpty(Q)判断队列Q是否为空,若为空返回1,否则返回0。,3.4.2 非循环队列和循环队列 分配一块连续的存储区域来存放队列里的
3、元素。由于队列的队头和队尾的位置是变化的,因而要设两个指针和分别指示队头和队尾元素在队列中的位置。,它们的初始值在队列初始化时均应置为。入队时将新元素插入所指的位置,然后尾指针加。出队时,删去所指的元素,然后头指针加并返回被删元素。由此可见,当头尾指针相等时队列为空。在非空队列里,头指针始终指向队头元素,而尾指针始终指向队尾元素的下一位置。,0 1 2 3,Frontrear,Front rear,(a)队列初始为空(b)A,B,C入队,front rear front rear(c)a出队(d)b,c出队,队为空,#define MAXQSIZE 100typedef struct Elem
4、Type dataMAXQSIZE;int front;/*队头位置*/int rear;/*队尾位置*/SqQueue;,顺序队列的操作演示,队列的顺序存储结构定义:,SqQueue QQ-front 存放即将要被删除的元素的下标。Q-rear 存放即将要被插入的元素(目前不在队列中)的下标。Q-data Q-front和Q-data Q-rear,初始化:Q.front=Q.rear=0队空:Q.front=Q.rear队满:Q.rear=maxsize(假溢出)求队长:入队:新元素按 rear 指示位置加入,再将队尾指针加一,即 rear=rear+1 出队:将front指示的元素取出,
5、再将队头指针加一,即front=front+1,非循环队列,注意:入队应考虑队满;出队应考虑队空。,和栈类似,队列中亦有上溢和下溢现象。此外,顺序队列中还存在“假上溢”现象。因为在入队和出队的操作中,头尾指针只增加不减小,致使被删除元素的空间永远无法重新利用。因此,尽管队列中实际的元素个数远远小于向量空间的规模,但也可能由于尾指针巳超出向量空间的上界而不能做入队操作。该现象称为假上溢。,为充分利用向量空间,克服上述假上溢现象,可以将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量,存储在其中的队列称为循环队列(Circular Queue)。在循环队列中进行出队、入队操作时,头尾指针仍
6、要加1,朝前移动。只不过当头尾指针指向向量上界(QueueSize-1)时,其加1操作的结果是指向向量的下界0。,显然,因为循环队列元素的空间可以被利用,除非向量空间真的被队列元素全部占用,否则不会上溢。因此,除一些简单的应用外,真正实用的顺序队列是循环队列(环形队列)。,入队和出队如何表示?,由于入队时尾指针向前追赶头指针,出队时头指针向前追赶尾指针,故队空和队满时头尾指针均相等。因此,我们无法通过Q-f=Q-r来判断队列“空”还是“满”。,front,J5,J6,J7,0,1,2,3,4,5,rear,front,J4,J9,J8,J4,J5,J6,0,1,2,3,4,5,rear,fro
7、nt,初始状态,队空:front=rear队满:front=rear,解决方案:1.另外设一个标志以区别队空、队满2.少用一个元素空间:队空:Q-front=Q-rear 队满:(Q-rear+1)%M=Q-front,队空:Q.front=Q.rear队满:Q.front=(Q.rear+1)%maxSize入队:Q.rear=(Q.rear+1)%maxSize出队:Q.front=(front+1)%maxSize;求队长:(Q.rear-Q.front+maxSize)%maxSize,循环队列,【例】设循环队列的容量为40(序号从0到39),现经过一系列的入队和出队运算后,有 fro
8、nt=11,rear=19;front=19,rear=11;问在这两种情况下,循环队列中各有元素多少个?,答:用队列长度计算公式:(Q.rear-Q.front+maxSize)%maxSize L=(401911)%40=8 L=(401119)%40=32,循环队列的操作演示,循环队列的基本运算实现 1、进队列 1)进队列算法,2)进队列实现程序 Status EnQueue(SqQueue,(1)检查队列是否已满,若队满,则进行溢出错误处理;(2)将新元素赋给队尾指针所指单元;(3)将队尾指针后移一个位置(即加1),指向下一单元。,2)出队列实现程序 Status DeQueue(Sq
9、Queue,2.出队列,1)出队列算法,(1)检查队列是否为空,若队空,则进行下溢错误处理;(2)取队首元素的值。(3)将队首指针后移一个位置(即加1);,(4)取队头元素ElemType GetHead(SqQueue Q)if(Q.front=Q.rear)return(ERROR);return(Q.dataQ.front);,(3)队列初始化 Q.front=Q.rear=0;,(5)判队列空否 int QueueEmpty(SqQueue Q)if(Q.front=Q.rear)reurn(1);else return(0);,3.4.3 链队列 队列的链式存储结构简称为链队列,它是
10、限制仅在表头删除和表尾插入的单链表。显然仅有单链表的头指针不便于在表尾做插入操作,为此再增加一个尾指针,指向链表的最后一个结点。于是,一个链队列由头指针和尾指针唯一确定。,null,*q,q.front,q.rear,非空队列,q.front,q.rear,null,空队列,*q,链队列示意图,和顺序队列类似,我们也是将这两个指针封装在一起,将链队列的类型LinkQueue定义为一个结构类型:typedef struct queuenode ElemType data;struct queuenode*next;QueueNode;typedef struct QueueNode*front;
11、QueueNode*rear;LinkQueue;,LinkQueue Q;Q-front 队列的头指针Q-rear 队列的尾指针,运算的实现,void InitQueue(LinkQueue,1、创建一个空队列:,2、队列的判空:int QueueEmpty(LinkQueue Q)return(Q.front-next=NULL,void EnQueue(LinkQueue,3、入队操作,null,*q,q.front,q.rear,x,null,p,4、出队操作:,Status DeQueue(LinkQueue,null,*q,q.rear,x,null,q.front,p,存储池,i
12、f(Q.rear=p)Q.rear=Q.front;free(p);return OK;,队列的应用,队列在日常生活中和计算机程序设计中,有着非常重要的作用,在此,仅举出两个方面例子来说明它,其它应用在后面章节中将会遇到。,第一个例子就是CPU资源的竞争问题。在具有多个终端的计算机系统中,有多个用户需要使用CPU各自运行自己的程序,它们分别通过各自终端向操作系统提出使用CPU的请求,操作系统按照每个请求在时间上的先后顺序,将其排成一个队列,每次把CPU分配给队头用户使用,当相应的程序运行结束,则令其出队,再把CPU分配给新的队头用户,直到所有用户任务处理完毕。,第二个例子就是主机与外部设备之间
13、速度不匹配的问题。以主机和打印机为例来说明,主机输出数据给打印机打印,主机输出数据的速度比打印机打印的速度要快得多,若直接把输出的数据送给打印机打印,由于速度不匹配,显然是不行的。所以解决的方法是设置一个打印数据缓冲区,主机把要打印输出的数据依此写如到这个缓冲区中,写满后就暂停输出,继而去做其它的事情,打印机就从缓冲区中按照先进先出的原则依次取出数据并打印,打印完后再向主机发出请求,主机接到请求后再向缓冲区写入打印数据,这样利用队列既保证了打印数据的正确,又使主机提高了效率。,讨论(本章小结),线性表、栈与队的异同点相同点:逻辑结构相同,都是线性的;都可以用顺序存储或链表存储;栈和队列是两种特
14、殊的线性表,即受限的线性表(只是对插入、删除运算加以限制)。不同点:运算规则不同,线性表为随机存取,而栈是只允许在一端进行插入和删除运算,因而是后进先出表LIFO;队列是只允许在一端进行插入、另一端进行删除运算,因而是先进先出表FIFO。用途不同,线性表比较通用;堆栈用于函数调用、递归和简化设计等;队列用于离散事件模拟、多道作业处理和简化设计等。,1、称正读和反读都相同的字符序列为“回文”,例如“abba”,写一个算法,判别读入以“”为结束符的字符序列是否为“回文”。2、假设以带头结点的循环链表表是队列,并且只设一个指针指向队尾结点,但不设头指针,设计相应的入队和出队算法。,int Test()/判别输入的字符串是否回文序列,是返回1,否返回0 Stack S;Queue Q;char a,b;InitStack(S);InitQueue(Q);while(c=getchar()!=)Push(S,c);EnQueue(Q,c);/同时使用栈和队列两种结构while(!StackEmpty(S)Pop(S,a);DeQueue(Q,b);if(a!=b)return ERROR;return OK;,1、试写一个算法判别读入的一个以为结束符的字符序列是否是“回文”。,