总结:
- 定义 : 只允许在一端进行插入操作, 而在另一端进行删除操作的线性表。 是一种FIFO的线性表。
- 顺序结构, 避免数组移动 , 和头指针前面空间的浪费, 使用循环队列
- 循环队列判断 空队列 和 队列满的两种方法
- 1.增加falg标记
- 2.在满时候 rear 和 front 之间空一个元素。
- 满的时候 : (rear + 1 ) % MAXSIZE == front
- 空的时候 : front == rear
- 队列的长度 : (rear - front + MAXSIZE) % MAXSIZE
- 顺序结构 和 链式结构的比较: 看最后;
定义:
队列(Queue) : 只允许在一端进行插入操作, 而在另一端进行删除操作的线性表。 是一种FIFO的线性表。
ADT:
队列的ADT: |
ADT Queue Data: 同线性表。元素具有相同的类型, 相邻的元素具有前驱 和 后继关系 Operations: InitQueue(*Q) : 初始化操作, 建立一个空的队列Q。 DestroyQueue(*Q) : 若队列存在, 则销毁它。 ClearQueue(*Q) : 将队列清空。 QueueEmpty(Q) : 若队列为空, 返回true, 否则返回false GetHead(Q, *e): 若队列非空, 用e返回队列Q的头元素 EnQueue(*Q, e): 若队列Q存在, 插入新的元素e到队列Q中并成为队尾巴元素。 DeQueue(*Q, *e): 删除队列Q中的队头元素, 并用e返回其值。 QueueLength(Q): 返回队列Q的元素个数。 endADT |
ADT->顺序存储->语言实现:
顺序存储的不足:
如果固定 下标为 0 的位置为 队头 , 则每次出队列的时候, 后面元素都要向前移动(时间
复杂度为O(n) , ), 因此使用 队头指针 和 队尾指针的方式 就能将出队列的操作时间复杂度降
低为O(1)
但是如果只是简单的使用头指针和尾巴指针会造成浪费空间滞留在顺序内存的前端, 也
就是虽然元素出队列, 但是头指针前的空间依然不能用的情况, 此时将顺序队列改装成 循环样
式的顺序队列解决空间浪费的问题。 【 也就是假溢出的问题 】
循环队列:
首位相接的顺序的结构,叫做循环队列。
但是当 队列满时候 和 队列为空的时候 均为 front == rear , 因此 :
- 使用一个标志flag , 未满时为0 , 满时候为1
- 在front 和 rear 之间留一个空位
- 当(rear+1) % QueueSize == front 时候为满, 否则为不满
公式:
计算队列中元素的公式 : (rear - front + QueueSize) % QueueSize
循环队列结构体定义 和 接口声明 文件名 : SqQueue.h |
/***************************************************************************** File name: SqQueue.h Description: 循环队列的结构体的定义和操作接口声明 Author: MusaGeek Version: 1.0 Date: 2018-11-26 *****************************************************************************/ #ifndef __SQQUEUE_H #define __SQQUEUE_H #define MAXSIZE 20 #define FALSE 0 #define TRUE 1 typedef int QElemType; typedef int BOOL; typedef unsigned int uint; /*循环队列的结构体*/ typedef struct { QElemType data[MAXSIZE]; /*顺序结构*/ int front; /*头指针*/ int rear; /*尾巴指针*/ }SqQueue; /*部分接口声明*/ extern BOOL InitQueue(SqQueue *Q); extern uint QueueLength(SqQueue Q); extern BOOL EnQueue(SqQueue *Q, QElemType e); extern BOOL DeQueue(SqQueue *Q, QElemType *e); #endif |
循环队列的操作实现: 文件名 : SqQueue.c |
说明 : 内部只有部分关键操作的实现 , 其他操纵的实现省略 |
/****************************************************** File name: SqQueue.c Description: 循环队列的操作的实现 Author: MusaGeek Version: 1.0 Date: 2018-11-26 Function List: ******************************************************/ #include <stdio.h> #include "SqQueue.h" /************************************************* Function: InitQueue Description: 初始化循环队列的头指针 和 尾指针 为0 Input: Q : 指向顺序队列的指针 Return: BOOL TRUE : 初始化成功 *************************************************/ BOOL InitQueue(SqQueue *Q) { Q->front = 0; Q->rear = 0; return TRUE; } /************************************************* Function: QueueLength Description: 获取队列的长度 公式 : (rear - front + MAXSIZE) % MAXSIZE; Input: Q : 循环队列的拷贝 Return: 循环队列的长度 uint -> unsigned int *************************************************/ uint QueueLength(SqQueue Q) { reuturn (Q.rear - Q.front + MAXSIZE) % MAXSIZE; } /************************************************* Function: EnQueue Description: 若队列未满则插入元素 1.判断队列未满否 , 否 返回FALSE 2.在rear指针处放置元素 3.rear 指针向后移动 4.返回TRUE 判断满的公式: 若 (rear + 1) % MAXSIZE == front 满 Input: Q : 指向循环队列的指针 e : 插入的元素 Return: BOOL TRUE:插入成功 FALSE:插入失败 *************************************************/ BOOL EnQueue(SqQueue *Q, QElemType e) { if((Q->rear + 1) % MAXSIZE == Q->front) /*判断队列满否*/ return FALSE; Q->data[Q->rear] = e; Q->rear = (Q->rear + 1) % MAXSIZE; return TRUE; } /************************************************* Function: DeQueue Description: 头指针指向的元素出队列 1.判断队列为空否 : 空则返回FALSE 2.将front 指针指向的元素通过e传出 3.front 指针向后移动 4.return TRUE Input: Q : 循环队列的拷贝 Output: e : 传出参数 , 头指针指向的元素 Return: BOOL TRUE : 出队列成功 FALSE : 出队列失败 *************************************************/ BOOL DeQueue(SqQueue *Q, QElemType *e) { if(Q->rear == Q->front) /*队列为空*/ return FALSE; *e = Q->data[Q->front]; Q->front = (Q->front + 1) % MAXSIZE; /*头指针向后移动*/ return TRUE; } |
ADT->链式存储->语言实现:
图解:
链式队列结构体定义 和 接口声明 文件名 : LInkQueue.h |
/***************************************************************************** File name: LinkQueue.h Description: 链式存储队列的结构体的定义和操作接口声明 Author: MusaGeek Version: 1.0 Date: 2018-11-26 *****************************************************************************/ #ifndef __LINKQUQUE_H #define __LINKQUQUE_H #define TRUE 1 #define FALSE 0 typedef int QElemType; /*假设队列存储的类型是int*/ typedef int BOOL; typedef unsigned int uint; /*链式存储中的结点结构*/ typedef struct QNode { QElemType data; struct QNode *next; }QNode, *QueuePtr; /*队列的链式结构*/ typedef struct { QueuePtr front, rear; /*队头, 队尾指针*/ }LinkQueue; /*部分接口说明*/ extern BOOL EnQueue(LinkQueue *Q, QElemType e); extern BOOL DeQueue(LinkQueue *Q, QElemType *e); extern BOOL InitQueue(LinkQueue *Q); #endif |
链式队列操作的实现 文件名 : LInkQueue.c |
说明 : 内部只有部分关键操作的实现 , 其他操纵的实现省略 |
/****************************************************** File name: LinkQueue.c Description: 队列链式存储的操作的实现 Author: MusaGeek Version: 1.0 Date: 2018-11-26 Function List: ******************************************************/ #include <stdio.h> #include <stdlib.h> #include "LinkQueue.h" /************************************************* Function: InitQueue Description: 初始化链式队列 创建链表的头结点 , 将front 和 rear 均指向头结点。 Input: Q: 指向链式队列的指针 Return: BOOL TRUE : 初始化成功 FALSE : 初始化失败 *************************************************/ BOOL InitQueue(LinkQueue *Q) { Q->front = (QueuePtr)malloc(sizeof(QNode)); if(!Q->front) return FALSE; Q->front->next = NULL; Q->rear = Q->front; } /************************************************* Function: EnQueue Description: 将元素插入队列 1.创建一个新结点 , 将e 赋值给结点的data 2.将创建的结点增加的链表的尾部 3.尾指针指向创建的结点 4.返回TRUE Input: Q: 指向链式队列的指针 Return: BOOL TRUE : 插入成功 FALSE : 插入失败 *************************************************/ BOOL EnQueue(LinkQueue *Q, QElemType e) { QueuePtr node = (QueuePtr)malloc(sizeof(QNode)); if(!node) return FALSE; node->data = e; node->next = NULL; Q->rear->next = node; /*将node结点插入链表尾部*/ Q->rear = node; /*将尾指针指向node*/ return TRUE; } /************************************************* Function: DeQueue Description: 头元素出队列 1.判断队列是否为空 , 为空 返回 FALSE 2.将头结点的 next 指向存放首元素的结点node的下一个结点 3.node结点的data通过e传出 4.若rear为node , 说明删除node以后队列为空 , 应该将rear 指向 front 5.释放node 6.返回TRUE Input: Q: 指向链式队列的指针 Output: e : 通过e传出首元素的值 Return: BOOL TRUE : 出队列成功 FALSE : 出队列失败 *************************************************/ BOOL DeQueue(LinkQueue *Q, QElemType *e) { if(Q->front == Q->rear) /*判断队列是否为空*/ return FALSE; QueuePtr node = Q->front->next; Q->front->next = node->next; /*从队列中删除node结点*/ if(Q->rear == node) /*判断是否队列删除node以后为空队列*/ Q->rear = Q->front; /*若为空队列,rear 指针 指向 front 指针指向的位置*/ *e = node->data; /*node结点的值传出, 完成出队列操作*/ free(node); return TRUE; } |
比较:
时间 : 如果频繁的插入 和 删除, 链式队列动态申请造成的时间开销 有影响, 虽然插入 和 删
除操作的时间复杂度均为1;
空间上 : 固定长度的循环队列可能造成空间上的浪费, 链式队列的会有两个指针的额外开销,
但是整体上的空间比较灵活。