队列(Queue)
来源:互联网 发布:魔法卡片收藏家软件 编辑:程序博客网 时间:2024/06/05 01:56
概念
队列(Queue)是只允许在一端进行插入工作,而在另一端进行删除操作的线性表。队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
队列的抽象数据类型:
ADT 队列(Queue)Data 同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。Operation InitQueue(*Q):初始化操作,建立一个空队列Q。 DestoryQueue(*Q):若队列Q存在,则销毁它。 ClearQueue(*Q):将队列Q清空。 QueueEmpty(Q):若队列为空,则返回true,否则返回false。 GetHead(Q,*e):若队列Q存在且非空,用e来返回队列Q的队头元素。 EnQueue(*Q,e):若队列Q存在,插入新元素e到队列Q中成为队尾元素。 DeQueue(*Q,*e):删除队列Q的队头元素,并用e返回其值。 QueueLength(Q):返回队列Q的元素个数。
队列的顺序存储
假设队列有n个元素,则顺序存储的队列需建立一个大于n的数组,并将n个元素存储在数组的前n个单元中,数组下标为0的一端为队头。所谓的插入队列操作,其实是在队尾追加一个元素,不需要移动任何元素,时间复杂度为
仔细想想,为何出队列是一定要全部移动呢?若不限制队列的元素不需要存储在数组的前n个单元,即队头不需再下标为0的位置,这样能提高队列出列的性能。
因此,引入两个指针,front指针指向队头元素,rear指针指向队尾指针元素的下移位置,这样当front等于rear时,该队列为空队列。
假设这个队列的总数不超过5个,但是因为数组末尾元素已经占用,再向后加,就会产生数组越界的错误,可实际上,队列在数组前面的位置还是空闲的,这种情况就是”假溢出”现象。
循环队列
要解决”假溢出”问题的方法就是后面满了,从头再开始,即头尾相接的循环,队列的这种头尾相接的顺序存储结构被称为循环队列。以上述为例,数组末尾元素占用后,将尾指针改为指向下标为0的位置,继续插入。
又有问题来了,空队列时,front==rear,而队列满时,还是front==rear,那如何判断队列是空还是满呢?
第一种方法,设置标志变量,当front==rear且flag==0时队列为空,当front==rear且flag==1时队列为满;
第二种方法,当front==rear时,队列为空,当队列满是,修改其条件,保留一个元素空间(当队列满是,数组中还有一个空闲单位)。假设队列的最大尺寸为QueueSize,则队列满时的条件为(rear+1)%QueueSize==front。(因为rear可能大于也可能小于font,但它们之间总是相差一个元素空间)
循环队列的顺序存储结构代码如下,部分方法也如下:
typedef int QElemType;//QElemType根据实际情况而定,这里设置为int/*循环队列的顺序存储结构*/typedef struct{ QElemType data[MAXSIZE]; int front; //头指针 int rear; //尾指针,若队列不为空,指向队尾元素的下一位置}SqQueue;/*初始化一个空队列*/Status InitQueue(SqQueue *Q){ Q->front=0; Q->rear=0; return OK;}/*返回Q的元素个数,即队列的长度*/int QueueLength(SqQueue Q){ return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;}/*若队列未满,则插入元素e为Q的新的队尾元素*/Status EnQueue(SqQueue *Q,QElemType e){ if((Q->rear+1)%MAXSIZE==Q->front) return ERROR;//队列满了 Q->data[Q->rear]=e; //插入队尾元素 //队尾指针后移一位,若到数组最后转向数组头部 Q->rear=(Q->rear+1)%MAXSIZE; return OK;}/*若队列不为空,则删除队列Q的队头元素,并用e返回其值*/Status DeQueue(SqQueue *Q,QElemType *e){ if(Q->rear==Q->front) return ERROR;//队列为空 *e=Q->data[Q->front]; //将队头元素赋值给e //队头指针后移一位,若到数组最后转向数组头部 Q->front=(Q->front+1)%MAXSIZE; return OK;}
若单是顺序存储,不是循环队列,算法的时间性能不高,但是循环队列又面临数组可能溢出问题,所以需要好好研究下队列的链式存储结构。
队列的链式存储结构
队列的链式存储结构,其实是线性表的单链表,只不过是只能尾进头出而已,将其简称为链队列。将队头指针指向链队列的头结点,队尾指针指向终端结点。当队列为空时,front和rear均指向头结点。
链队列的结构代码如下:
typedef int QElemType;typedef struct QNode{ //结点结构 QElemType data; struct QNode *next;}QNode,*QueuePtr;typedef struct{ //链队列结构 QueuePtr front,rear; /*队头、队尾指针*/}LinkQueue;
下面详细介绍链队列的入队、出队操作及具体代码:
/*插入元素e为Q新的队尾元素*/Status EnQueue(LinkQueue *Q,QElemType e){ //首先生成一个新的元素 QueuePtr s=(QueuePtr)malloc (sizeof(QNode)); if(! s) exit(OVERFLOW); //存储分配失败 s->data=e; //设置元素的值 s->next=NULL; //末尾元素,下一结点为空 Q->rear->next=s; //插入新元素,将原队尾元素的指针域指向新结点 Q->rear=s; //将队尾指针后移,指向新元素 return OK;}/*若队列不为空时,删除队列Q的队头元素,并用e来返回其值,并返回OK,否则返回ERROR*/Status DeQueue(LinkQueue *Q,QElemType *e){ QueuePtr p; if(Q->front==Q->rear) return ERROR; //队列为空 p=Q->front->next; //要删除的结点 *e=p->data; //取得要删除结点的值 Q->front->next=p->next; //删除队头元素结点 //若队头是队尾,则删除后将尾指针指向头结点,注意只有一个元素的情况 if(Q->rear==p) Q->rear=Q->front; free(p); return OK;}
循环队列与链队列的对比:
时间上,基本操作均为常数时间O(1) ,不过循环队列事先申请存储空间,且使用期间不释放,而链队列,每次申请和释放结点均会产生时间开销,若入出队频繁,二者还是存在差异的。就空间上来说,循环队列有一固定长度,所以存在空间浪费的情况,虽然链队列不存在这个情况,但会产生空间开销,在可接受范围内,所以空间上链队列要更灵活些。总而言之,在能确定队列长度最大值的情况下,建议使用循环队列,如果无法预估队列长度时,使用链队列。
- STL--queue(队列)
- 队列(Queue)
- 数据结构-队列(queue)
- C# 队列(Queue)
- STL - queue(队列)
- 队列(Queue)
- C#队列(Queue)
- C# 队列(Queue)
- 队列(Queue)
- 队列(queue)数据结构
- 队列(Queue)
- 队列(queue)原理
- 队列(queue)
- 队列(Queue)
- 队列(Queue)
- 顺序队列(Queue)
- 队列(queue)
- 队列(Queue)
- 反射入门
- 有关虚函数(不能声明的和需要极力避免的)
- ios 原生应用和React native 结合开发1.环境搭建
- wwww
- 文本文件和二进制文件的区别
- 队列(Queue)
- 数列
- MFC中自定义消息的步骤
- Python is和==的区别
- Unhandled exception in al.exe(KERNELBASE.DLL):0xE06D7363:Microsoft C++Exception
- 利用python pytesseract进行OCR(验证码识别)
- 字符串反转
- 关于Activex控件中创建含有子控件的对话框失败问题
- NumPy数组