队列和循环队列

队列

概念及结构

只允许在一端进行插入数据,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性(First In First Out)

队尾:进行插入操作的一端

队头:进行删除操作的一端

结构示意图:

队列演示

队列的代码实现

本篇就以单链表(不带哨兵位)为例来实现队列

队列结点的定义

typedef int QDataType;	
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;

为了方便找到单链表的的尾结点,实现队列的插入数据,这里再定义一个结构体Que

typedef struct Queue
{
	QNode* head;
	QNode* tail;
    int size;
}Que;

定义这样一个结构体的优点:

实现队列的插入和删除数据不需要传二级指针,只需要传Que的结构体指针就可以对队列进行操作

队列的相关操作函数

void QueueInit(Que* pq);				// 队列初始化函数
void QueueDestroy(Que* pq);				// 队列销毁函数
void QueuePush(Que* pq, QDataType x);	// 增加数据
void QueuePop(Que* pq);					// 删除数据
QDataType QueueFront(Que* pq);			// 取队头数据
QDataType QueueBack(Que* pq);			// 取队对尾数据
bool QueueEmpty(Que* pq);				// 判断队列是否为空
int QueueSize(Que* pq);					// 返回队列数据个数

在这里着重说一下QueuePush函数和QueuePop函数

QueuePush():

void QueuePush(Que* pq, QDataType x)	// 增加数据
{
	assert(pq);

	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	
	newNode->data = x;		// 将要插入的数据
	newNode->next = NULL;	// 新建结点的next指针指向NULL

    // 连接结点分两种情况
	if (pq->tail == NULL)	//队列中没有数据
	{
		pq->head = pq->tail = newNode;
	}
	else					// 队列中有数据
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
	pq->size++;
}

插入数据,先新建一个结点newNode,然后链如队列里边,注意分两种情况:队列中没有数据和队列中有数据,可以用队列的tail指针是否为空来进行判断,最后不要忘记处理队列中的size变量的更新。

QueuePop():

void QueuePop(Que* pq)					// 删除数据
{
	assert(pq != NULL);					// pq不能为空
	assert(pq->head != NULL);			// 队列中不能没有数据

	if (pq->head == pq->tail) 			// 剩了一个结点,单独判断
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else								// 剩了大于一个结点
	{
		QNode* del = pq->head;
		pq->head = pq->head->next;
		free(del);
	}
	pq->size--;
}

删除队列数据,需要从队头进行删除,队列中需要有数据。删除队头数据需要分两种情况:

  1. 队列中只剩下一个数据,删除后不仅需要处理pq->head,也需要处理pq->tail,否则pq->tail是野指针。
  2. 队列中还有多个数据(大于一个),只需要对队列的尾结点释放即可,不用更改头指针pq->head

最后都需要更新pq->size

循环队列

将队列的收尾相连从逻辑上看形成一个环,称为环形队列或者循环队列

逻辑图:

循环队列

循环队列的实现可以使用数组也可以使用链表实现,不过使用链表有以下缺点

  • 对于单链表实现的队列,取队尾的数据不好取。
    • 解决方法:可以用双向循环队列
  • 不好区分队列是空还是满的状态。
    • 解决方法:始终空一个数据的位置,front==rear就是空,front==rear->next就是队列满

下面就以数组方式来实现循环队列

始终多开一个空间,该队列最多可以存放4个数据,在这个却开辟5个数据空间,用来区分队列空、队列满的状态

队空

在这个图片中可以看出front==rear,此时为队列空。

队列满2

上方这个就是队列满的状态 front==rear+1,我们还要考虑当rear在最后一个的时候的情况,也就是下图:

队列满1

此时队列满的条件就是(rear+1)%MAX==front,这里的MAX是数组的长度5

循环队列结构体

#define MAX 5
typedef int QDataType;
typedef struct CircularQueue
{
	QDataType* a;
	int front;
	int rear;
	int k;
}CQue;

队列的相关操作函数

void CircularQueueInit(CQue* pq);					// 循环队列初始化
bool CircularQueueEmpty(CQue* pq);					// 判空
bool CircularQueueFull(CQue* pq);					// 判满
bool CircularQueueEnQueue(CQue* pq, QDataType x);	// 入数据
bool CircularQueueDeQueue(CQue* pq);				// 出数据
QDataType CircularQueueFront(CQue* pq);				// 取队头数据
QDataType CircularQueueRear(CQue* pq);				// 取队尾数据
void CircularQueueFree(CQue* pq);					// 空间释放

其中,入数据和出数据函数都要考虑边界。

对于取队尾数据

QDataType CircularQueueRear(CQue* pq);				// 取队尾数据

需要取到pq->rear前一个,在这里统一采用**(pq->rear+MAX-1)%mAX**

相关源码
Queue.h
#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>

typedef int QDataType;
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Que;

void QueueInit(Que* pq);				// 队列初始化函数
void QueueDestroy(Que* pq);				// 队列销毁函数
void QueuePush(Que* pq, QDataType x);	// 增加数据
void QueuePop(Que* pq);					// 删除数据
QDataType QueueFront(Que* pq);			// 取队头数据
QDataType QueueBack(Que* pq);			// 取队对尾数据
bool QueueEmpty(Que* pq);				// 判断队列是否为空
int QueueSize(Que* pq);					// 返回队列数据个数
Queue.c
#include "Queue.h"
void QueueInit(Que* pq)				// 队列初始化函数
{
	assert(pq != NULL);
	pq->head = pq->tail = NULL;
	pq->size = 0;
}
void QueueDestroy(Que* pq)				// 队列销毁函数
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur != NULL)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

void QueuePush(Que* pq, QDataType x)	// 增加数据
{
	assert(pq);

	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	
	newNode->data = x;
	newNode->next = NULL;

	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
	pq->size++;
}

void QueuePop(Que* pq)					// 删除数据
{
	assert(pq != NULL);
	assert(pq->head != NULL);

	if (pq->head == pq->tail) // 剩了一个结点,单独判断
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* del = pq->head;
		pq->head = pq->head->next;
		free(del);
	}
	pq->size--;
}

QDataType QueueFront(Que* pq)			// 取队头数据
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

QDataType QueueBack(Que* pq)			// 取队对尾数据
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

bool QueueEmpty(Que* pq)				// 判断队列是否为空
{
	assert(pq);
	return pq->head == NULL;
}

int QueueSize(Que* pq)					// 返回队列数据个数
{
	assert(pq);
	return pq->size;
}
CircularQueue.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

#define MAX 5

typedef int QDataType;
typedef struct CircularQueue
{
	QDataType* a;
	int front;
	int rear;
	int k;
}CQue;


void CircularQueueInit(CQue* pq);					// 循环队列初始化
bool CircularQueueEmpty(CQue* pq);					// 判空
bool CircularQueueFull(CQue* pq);					// 判满
bool CircularQueueEnQueue(CQue* pq, QDataType x);	// 入数据
bool CircularQueueDeQueue(CQue* pq);				// 出数据
QDataType CircularQueueFront(CQue* pq);				// 取队头数据
QDataType CircularQueueRear(CQue* pq);				// 取队尾数据
void CircularQueueFree(CQue* pq);					// 空间释放
CircularQueue.c
#include"CircularQueue.h"

void CircularQueueInit(CQue* pq)
{
	assert(pq);
	// 多开一个空间,方便区分空和满
	pq->a = (QDataType*)malloc(sizeof(QDataType)*MAX);
	if (pq->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	pq->front = pq->rear = 0;
	pq->k = MAX - 1;
}

bool CircularQueueEmpty(CQue* pq)
{
	assert(pq);
	return pq->front == pq->rear;
}

bool CircularQueueFull(CQue* pq)
{
	assert(pq);
	return (pq->rear + 1) % MAX == pq->front;
}

bool CircularQueueEnQueue(CQue* pq, QDataType x)
{
	if (CircularQueueFull(pq))
		return false;
	pq->a[pq->rear] = x;
	pq->rear++;

	//if (pq->rear == MAX)
	//	pq->rear = 0;

	pq->rear %= MAX;

	return true;
}

bool CircularQueueDeQueue(CQue* pq)
{
	assert(pq);
	if (CircularQueueEmpty(pq))
		return false;
	pq->front++;
	pq->front %= MAX;
	return true;
}

QDataType CircularQueueFront(CQue* pq)
{
	assert(pq);
	assert(!CircularQueueEmpty(pq));
	return pq->a[pq->front];
}

QDataType CircularQueueRear(CQue* pq)
{
	assert(pq);
	assert(!CircularQueueEmpty(pq));

	return pq->a[(pq->rear + pq->k) % MAX];
}

void CircularQueueFree(CQue* pq)
{
	assert(pq);
	free(pq->a);
	pq->a = NULL;
}
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值