数据结构(七)—— 队列

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语言项目去熟悉和了解算法跟操作。当然这是我的一点体会。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值