1.队列的相关概念即用途
队列是只能在表的一端进行插入运算,在表的另外一端进行删除运算的线性表(先进先出),实现采用循环顺序队列更为简便,因为只能在队首和队尾进行运算,所以采用循环顺序队列的时候可以循环地实现队列的插入与输出。经常用圆圈来表示,如下图:
常见用途:
- 脱机打印输出:按申请的先后顺序依次输出。
- 多用户系统中,多个用户排队,分时地循环使用CPU和主存(分时管理系统)
- 按用户的优先级排成多个队,每个优先级一个队列
- 实时控制系统中,信号按接受的先后顺序依次处理
- 网络电文传输,按到达的时间先后顺序依次进行
计算机操作指令也是按照顺序进行执行的,因此队列在计算机应用中体现在很多的地方。
2.队列的定义
队列因为是在其表的表头和表尾进行操作,因此定义队列时要对其表头表尾进行定义,而顺序队列和链表队列的定义又有所不同。
顺序队列定义:定义数据的头指针,跟表头指针表尾指针,就可以对整个队列进行操作
typedef struct {
QElemType* base; //初始化的动态分配存储空间
int front; //头指针
int rear; //尾指针
}SqQueue;
链表队列定义:定义节点的内容,即节点的数据域跟指针域。在定义队列的头指针跟尾指针,指向链表的头跟为:如图所示:
typedef struct Qnode {
QElemType data;
struct Qnode* next;
}QNode,*QueuePtr;
typedef struct {
QueuePtr front; //队头指针
QueuePtr rear; // 队尾指针
}LinkQueue;
3.队列的相关操作以及实现
队列主要的就是入队跟出队操作,因此对这两个操作进行介绍,并从顺序队列和链式队列两种实现方法进行实现:
顺序队列实现:
顺序队列的入队操作:下列三图分别为初始的队列,跟入队后,以及出队后的图:
建立队列后,左图中队列的表头指针跟表尾指针就指在同一个初始位置,然后通过移动队尾的指针将元素插入到队尾如中图,出队的顺序是通过将队头的指针向队尾移动如右图。因此如果不将其设置为循环队列的话,则会浪费很多空的内存块。
循环队列如下图所示,虚拟成一个圈。
则添加操作,就需要对整个队列的整体长度机型mod运算;
例如:现在是6个元素,如图要插入到1的位置,则是rear%MAXSIZE
也就是7%6就为1(图中已经++一圈了,所以rear现在的位置应该为7;
而我们在进行计算的时候,常常将一块内存进行浪费,以获得队空跟队满的区别:
假如全部块都作为存储块:
队空:Q.front == Q.rear
队满:Q.front == Q.rear
假如有一块内存是空的,即Q.rear指向空内存块
队空: Q.front == Q.rear.next
队满:Q.front.next == Q.rear
因此插入操作在有空内存的时候为:
Q.base [Q.rear] = e;
Q.rear = (Q.rear+1)%MAXSIZE;
而出队的操作为,将其头指针向前移动(Q.front+1)%MAXSIZE的操作:
具体实现如下:
头文件:
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define FALSE 0
#define OVERFLOW -1
typedef int QElemType;
typedef int Status;
#define MAXQSIZE 100
typedef struct {
QElemType* base; //初始化的动态分配存储空间
int front; //头指针
int rear; //尾指针
}SqQueue;
Status InitQueue(SqQueue* Q); //构造空队列Q
Status QueueEnpty(SqQueue Q); //判断队列是否为空
int QueueLength(SqQueue Q); //队列长度
int GetHead(SqQueue Q,QElemType* e); //返回队头元素
Status EnQueue(SqQueue* Q, QElemType* e); //入队
Status DeQueue(SqQueue* Q, QElemType* e); //出队
Status PrintQueue(SqQueue Q);
Status ClearQueue(SqQueue* Q); //清空队列Q
Status DestroyQueue(SqQueue* Q); //销毁队列Q
函数实现文件
#include "Queue.h"
Status InitQueue(SqQueue* Q) //构造空队列Q
{
Q->base = (QElemType*)malloc(sizeof(QElemType) * MAXQSIZE);
if (!Q->base)
return OVERFLOW;
Q->front = Q->rear = 0;
return OK;
}
Status QueueEnpty(SqQueue Q) //判断队列是否为空
{
if (Q.rear == Q.front) {
return OK;
}
else {
return FALSE;
}
}
int QueueLength(SqQueue Q) //队列长度
{
return ((Q.rear - Q.front + MAXQSIZE) % MAXQSIZE);
}
QElemType GetHead(SqQueue Q, QElemType* e) //返回队头元素
{
if (QueueEnpty) {
return Q.base[Q.front];
}
else {
return FALSE;
}
}
Status EnQueue(SqQueue* Q, QElemType* e) //入队
{
if ((Q->rear+1)%MAXQSIZE == Q->front)
{
return FALSE;
}
else {
Q->base[Q->rear] = *e;
Q->rear = (Q->rear+1)%MAXQSIZE;
return OK;
}
}
Status DeQueue(SqQueue* Q, QElemType* e) //出队
{
if (!QueueEnpty(*Q))
{
*e = Q->base[Q->front];
Q->front=(Q->front+1)%MAXQSIZE;
return OK;
}
else {
return FALSE;
}
}
Status PrintQueue(SqQueue Q)
{
int count = QueueLength(Q);
if (count == 0)
return FALSE;
else {
printf("当前队列为:");
while (count)
{
printf("%d ", Q.base[Q.front++]);
count--;
}
return OK;
}
}
Status ClearQueue(SqQueue* Q) //清空队列Q
{
while (!QueueEnpty(*Q)) {
Q->front = (Q->front + 1) % MAXQSIZE;
}
return OK;
}
Status DestroyQueue(SqQueue* Q) //销毁队列Q
{
ClearQueue(Q);
if (Q->base)
{
free(Q->base);
}
return OK;
}
主函数文件
#include "Queue.h"
int main()
{
SqQueue Q;
QElemType e;
InitQueue(&Q);
if (QueueEnpty(Q))
{
printf("队列是空的!!!\n");
}
for (int i = 0; i < 5; i++)
{
e = i;
EnQueue(&Q, &e);
}
int len = QueueLength(Q);
printf("当前队列长度为:%d\n", len);
if (PrintQueue(Q))
{
printf("\n");
}
else {
printf("队列已空!!!\n");
}
QElemType E;
for (int i = 0; i < 4; i++)
{
DeQueue(&Q, &E);
printf("已出队元素为:%d\n", E);
}
E = GetHead(Q, &E);
printf("队头元素为:%d\n", E);
if (PrintQueue(Q))
{
printf("\n");
}
else {
printf("队列已空!!!\n");
}
ClearQueue(&Q);
if (PrintQueue(Q))
{
printf("\n");
}
else {
printf("队列已空!!!\n");
}
DestroyQueue(&Q);
}
链式队列实现:
链式队列采用带头结点的链表进行实现。插入操作则是通过链表的尾插法实现,而出队操作也是跟单链表的操作类似。可以用下图进行解释:
初始化链表:将头尾指针都指向头节点
插入操作:尾插法
出队操作:链表的删除节点操作
具体代码实现如下:
头文件
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define FALSE 0
#define OVERFLOW -1
typedef int QElemType;
typedef int Status;
#define MAXQSIZE 100
typedef struct Qnode {
QElemType data;
struct Qnode* next;
}QNode,*QueuePtr;
typedef struct {
QueuePtr front; //队头指针
QueuePtr rear; // 队尾指针
}LinkQueue;
Status InitQueue(LinkQueue* Q); //构造空队列Q
Status QueueEnpty(LinkQueue Q); //判断队列是否为空
int GetHead(LinkQueue Q); //返回队头元素
Status EnQueue(LinkQueue* Q, QElemType* e); //入队
Status DeQueue(LinkQueue* Q, QElemType* e); //出队
Status PrintQueue(LinkQueue Q); //打印队列
Status DestroyQueue(LinkQueue* Q); //销毁队列Q
函数实现文件
#include "LQueue.h"
Status InitQueue(LinkQueue* Q) //构造空队列Q
{
Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode));
Q->front->next = NULL;
return OK;
}
Status QueueEnpty(LinkQueue Q) //判断队列是否为空
{
if (Q.rear == Q.front) {
return OK;
}
else {
return FALSE;
}
}
int GetHead(LinkQueue Q) //返回队头元素
{
if (!QueueEnpty(Q))
{
return Q.front->next->data;
}
else {
return 0;
}
}
Status EnQueue(LinkQueue* Q, QElemType* e) //入队
{
QueuePtr new;
new = (QueuePtr)malloc(sizeof(QNode));
new->data = *e;
new->next = NULL;
Q->rear->next = new;
Q->rear = new;
return OK;
}
Status DeQueue(LinkQueue* Q, QElemType* e) //出队
{
QueuePtr p;
if (!QueueEnpty(*Q)) {
p = Q->front->next;
*e = p->data;
Q->front->next = p->next;
if (Q->rear == p) {
Q->rear = Q->front;
}
free(p);
return OK;
}
else {
return FALSE;
}
}
Status PrintQueue(LinkQueue Q) //打印队列
{
QNode p;
QNode* q = Q.front->next;
printf("当前元素队列为:");
while (Q.front->next) {
p = *Q.front->next;
Q.front->next = p.next;
printf("%d ", p.data);
}
Q.front->next = q;
return OK;
}
Status DestroyQueue(LinkQueue* Q) //销毁队列Q
{
while (Q->front) {
QueuePtr p = Q->front->next;
free(Q->front);
Q->front = p;
}
return OK;
}
主函数实现
#include "LQueue.h"
int main()
{
LinkQueue Q;
QElemType e;
InitQueue(&Q);
//打印队列
if (QueueEnpty(Q)) {
printf("队列是空的!!!\n");
}
else {
PrintQueue(Q);
printf("\n");
}
//入队
for (int i = 0; i < 5; i++)
{
e = i;
EnQueue(&Q, &e);
}
//打印队列
if (QueueEnpty(Q)) {
printf("队列是空的!!!\n");
}
else {
PrintQueue(Q);
printf("\n");
}
//打印出队的元素
DeQueue(&Q, &e);
printf("出队的元素为:%d\n", e);
//打印队头元素
if (GetHead(Q))
{
printf("队头元素为:%d\n", GetHead(Q));
}
//打印队列
if (QueueEnpty(Q)) {
printf("队列是空的!!!\n");
}
else {
PrintQueue(Q);
printf("\n");
}
//销毁队列
DestroyQueue(&Q);
return 0;
}
总结: 队列是特殊的线性表,栈也是,因此在代码实现方面从顺序存储和链式存储都大同小异,而单链表就是比较重要的部分,因为C语言指针方面学的不是很扎实,因此在解决链表方面花费时间较多一些。因此如果想要数据结构学得轻松一点可以先将C语言学得扎实一点,通过多一些的C语言项目去熟悉和了解算法跟操作。当然这是我的一点体会。