数据结构与算法之如何基于顺序存储和链式存储设计一个队列
前言
上一篇,我们学习了栈的结构,以及基于顺序存储
和链式存储
两个不同角度如何设计一个栈,以及一些对栈
的常规操作。
那么栈本篇来看一下队列
的结构,以及如何基于顺序存储
、链式存储
两个不同角度设计一个队列
。
1. 队列的结构
队列
是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)
进行删除操作,而在表的后端(rear)
进行插入操作。
和栈
一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列的示意图:
当队列
以顺序存储的方式存储时:
假如长度为 5 的队列,初始状态,空队列如所示,front
与rear
指针均指向下标为 0 的位置。然后依次入队C1
、C2
、C3
,front
指针依然指向下标为 0 位置,而rear
指针指向下标为 3 的位置。然后依次C1
、C2
出队,移动front
到 2 的位置,然后C4
、C5
、C6
入队,C3
出队,就会形成下面图的形式
这时,rear
后面没有可以入队的空间,由于队列
的规则,只能从一段入队,导致front
前面的位置无法存储数据,而形成队列已满的形式,我们称这种情况为 假溢出。
为了防止 假溢出,我们可以设计一个循环队列,如下图:
我们可以牺牲一个存储单元,来判断是否满对。
判断空对:Q.front = Q.rear
判断满对:Q.front = (Q.rear + 1) % MaxSize
2. 顺序存储队列的设计
2.1 顺序存储队列的设计
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 // 存储空间初始化分配量
typedef int Status;
typedef int QElemType;
// 循环队列的顺序存储结构
typedef struct {
QElemType data[MAXSIZE];
int front; // 头指针
int rear; // 尾指针,若队列不空,指向队列尾元素的下一个位置
}SqQueue;
判断空对:Q.front = Q.rear
判断满对:Q.front = (Q.rear + 1) % MaxSize
2.2 顺序存储队列的基本操作
- 初始化一个空队列
// 初始化一个空队列
Status InitQueue(SqQueue *Q) {
Q->front = 0;
Q->rear = 0;
return OK;
}
- 清空队列
// 清空队列
Status ClearQueue(SqQueue *Q) {
Q->front = Q->rear = 0;
return OK;
}
- 判断队列是否为空
Status QueueEmpty(SqQueue Q) {
if (Q.front == Q.rear) { // 空队列标记
return TRUE;
} else {
return FALSE;
}
}
- 返回元素个数,即:队列的当前长度
int QueueLength(SqQueue Q) {
// + MAXSIZE,循环队列,防止出现负数
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
- 返回队列
Q
的队头元素
Status GetHead(SqQueue Q, QElemType *e) {
// 判断队列是否为空
if (Q.front == Q.rear) {
return ERROR;
}
*e = Q.data[(Q.front)];
return OK;
}
- 入队
Status EnQueue(SqQueue *Q, QElemType e) {
// 判断是否队列已满
if ((Q->rear + 1) % MAXSIZE == Q->front) {
printf("队列已满\n");
return ERROR;
}
// 将元素赋值给队尾
Q->data[Q->rear] = e;
// rear指针后移,若到最后则转到数组头部
Q->rear = (Q->rear + 1)%MAXSIZE;
return OK;
}
- 出队
Status DeQueue(SqQueue *Q, QElemType *e) {
// 判断是否为空
if (Q->rear == Q->front) {
return ERROR;
}
// 队头元素赋值给e
*e = Q->data[Q->front];
//front 指针向后移动一位,若到最后则转到数组头部
Q->front = (Q->front+1)%MAXSIZE;
return OK;
}
- 遍历队列元素
Status QueueTraverse(SqQueue Q) {
int i;
i = Q.front;
while (i != Q.rear) {
printf("%d ", Q.data[i]);
i = (i + 1)%MAXSIZE;
}
printf("\n");
// int e;
// for (i = Q.front; i<Q.rear; i++){
// e = Q.data[i];
// printf(" %d ", e);
// }
// printf("\n");
return OK;
}
3. 链式存储队列的设计
3.1 链式存储队列的设计
链式存储的队列,需要设计一个节点
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int QElemType;
// 节点结构
typedef struct QNode {
struct QNode *next;
QElemType data;
}QNode, *QueuePtr;
// 队列的链表结构
typedef struct {
QueuePtr front; // 头指针
QueuePtr rear; // 尾指针
}LinkQueue;
3.2 链式存储队列的基本操作
- 初始化队列
Status InitQueue(LinkQueue *Q) {
// 头尾指针指向新节点
Q->front = Q->rear = (QueuePtr )malloc(sizeof(QNode));
// 判断是否创建成功
if (!Q->front) {
return ERROR;
}
// 头结点的指针域置空
Q->front->next = NULL;
return OK;
}
- 销毁队列
Status DestoryQueue(LinkQueue *Q) {
// 遍历节点,释放
while (Q->front) {
// 尾指针已经失去意义,作为临时遍历使用
Q->rear = Q->front->next;
free(Q->front);
Q->front = Q->rear;
}
return OK;
}
- 队列置空
Status ClearQueue(LinkQueue *Q) {
QueuePtr p,q;
// 头尾指针相等
Q->rear = Q->front;
p = Q->front->next;
Q->front->next = NULL;
while (p) {
q = p;
p = p->next;
free(q);
}
return OK;
}
- 判断是否为空,即判断头尾节点是否相等
Status QueueEmpty(LinkQueue Q){
if (Q.front == Q.rear)
return TRUE;
else
return FALSE;
}
- 获取长度
int QueueLength(LinkQueue Q) {
int i = 0;
QueuePtr p = Q.front;
while (p != Q.rear) {
i ++;
p = p->next;
}
return i;
}
也可以在设计队列
的时候,加一个变量count
,入队
和出队
时 +1
或 -1
,来计数。
- 入队 不用判断是否满队
Status EnQueue(LinkQueue *Q, QElemType e) {
// 创建新节点
QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
if (!s) {
return ERROR;
}
s->data = e;
s->next = NULL;
// 出入队尾
Q->rear->next = s;
//修改队尾指针
Q->rear = s;
return OK;
}
- 出队 需要判断是否为空
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;
//若队头就是队尾,则删除后将rear指向头结点.
if(Q->rear == p) Q->rear = Q->front;
free(p);
return OK;
}
- 获取头元素
Status GetHead(LinkQueue Q,QElemType *e) {
//队列非空
if (Q.front != Q.rear) {
//返回队头元素的值,队头指针不变
*e = Q.front->next->data;
return TRUE;
}
return FALSE;
}
- 遍历元素
Status QueueTraverse(LinkQueue Q){
QueuePtr p;
p = Q.front->next;
while (p) {
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return OK;
}
···