目录
1. 前言:
在之前我们学习了栈,我们知道栈的特点是后进先出,我们今天学习的队列,它是先进先出的。
2. 队列
2.1 队列的概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

2.2 队列的实现
其实队列和栈一样,也是可以使用顺序表和单链表来实现的,这里本章主要讲述使用单链表来实现队列。
Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}qn;
typedef struct Queue
{
	//记录队头
	qn* phead;
	//记录队尾
	qn* ptail;
	//记录队列有效数据个数
	int size;
}q;
//初始化队列
void QInit(q* pq);
//入队
void QPush(q* pq, QDataType x);
//出队
void QPop(q* pq);
//获取队头元素
QDataType QFront(q* pq);
//获取队尾元素
QDataType QBack(q* pq);
//获取队列内有效数据个数
int QSize(q* pq);
//判断队列是否为空
bool QEmpty(q* pq);
//打印队列
void QPrint(q* pq);
//销毁队列
void QDestroy(q* pq);
 
2.3 队列的声明
Queue.h
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}qn;
typedef struct Queue
{
	//记录队头
	qn* phead;
	//记录队尾
	qn* ptail;
	//记录队列有效数据个数
	int size;
}q; 
这里我们跟链式栈的声明方式一样,使用了两个结构体,一个结构体用来声明节点的结构,一个结构体声明队列的结构,这里在队列结构中,我们增加指向队头和队尾的指针和计数的size,这样可以方便我们迅速的找到队头数据和队尾数据还有队内有效数据个数。
2.4 队列的初始化
Queue.c
void QInit(q* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
} 
2.5 队列的入队
Queue.c
void QPush(q* pq, QDataType x)
{
	assert(pq);
	qn* newnode = (qn*)malloc(sizeof(qn));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
} 
2.6 队列的出队
void QPop(q* pq)
{
	assert(pq);
	assert(pq->size > 0);
	//只有一个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	//多个节点
	else
	{
		qn* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
} 
2.7 队列获取队头元素
Queue.c
QDataType QFront(q* pq)
{
	assert(pq);
	assert(pq->size > 0);
	return pq->phead->data;
} 
2.8 队列获取队尾元素
Queue.c
QDataType QBack(q* pq)
{
	assert(pq);
	assert(pq->size > 0);
	return pq->ptail->data;
} 
2.9 队列获取有效数据个数
Queue.c
int QSize(q* pq)
{
	assert(pq);
	return pq->size;
} 
2.10 队列判断是否为空
Queue.c
bool QEmpty(q* pq)
{
	assert(pq);
	return pq->size == 0;
} 
2.11 打印队列
Queue.c
void QPrint(q* pq)
{
	assert(pq);
	while (!QEmpty(pq))
	{
		printf("%d ", QFront(pq));
		QPop(pq);
	}
} 
2.12 销毁队列
Queue.c
void QDestroy(q* pq)
{
	assert(pq);
	qn* pcur = pq->phead;
	while (pcur)
	{
		qn* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
} 
3. 队列完整代码
3.1 Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}qn;
typedef struct Queue
{
	//记录队头
	qn* phead;
	//记录队尾
	qn* ptail;
	//记录队列有效数据个数
	int size;
}q;
//初始化队列
void QInit(q* pq);
//入队
void QPush(q* pq, QDataType x);
//出队
void QPop(q* pq);
//获取队头元素
QDataType QFront(q* pq);
//获取队尾元素
QDataType QBack(q* pq);
//获取队列内有效数据个数
int QSize(q* pq);
//判断队列是否为空
bool QEmpty(q* pq);
//打印队列
void QPrint(q* pq);
//销毁队列
void QDestroy(q* pq);
 
3.2 Queue.c
#include"Queue.h"
void QInit(q* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
void QPush(q* pq, QDataType x)
{
	assert(pq);
	qn* newnode = (qn*)malloc(sizeof(qn));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
void QPop(q* pq)
{
	assert(pq);
	assert(pq->size > 0);
	//只有一个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	//多个节点
	else
	{
		qn* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}
QDataType QFront(q* pq)
{
	assert(pq);
	assert(pq->size > 0);
	return pq->phead->data;
}
QDataType QBack(q* pq)
{
	assert(pq);
	assert(pq->size > 0);
	return pq->ptail->data;
}
int QSize(q* pq)
{
	assert(pq);
	return pq->size;
}
bool QEmpty(q* pq)
{
	assert(pq);
	return pq->size == 0;
}
void QPrint(q* pq)
{
	assert(pq);
	while (!QEmpty(pq))
	{
		printf("%d ", QFront(pq));
		QPop(pq);
	}
}
void QDestroy(q* pq)
{
	assert(pq);
	qn* pcur = pq->phead;
	while (pcur)
	{
		qn* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
} 
4. 循环队列
4.1 循环队列的概念
循环队列:循环队列是一种线性数据结构,其操作表示基于FIFO(先进先出)原则,并且队尾被连接在队头之后以形成一个循环,前提是它的空间大小是固定的。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
简单来说就是:有限的空间,保证先进先出,重复使用。
4.2 循环队列的实现
 
CircularQueue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#define k 4
typedef int CQDataType;
typedef struct CircularQueue
{
	CQDataType* arr;
	int front;//指向队头
	int rear;//指向队尾的下一个位置
}cq;
//初始化
void CqInit(cq* pcq);
//判空
bool CqEmpty(cq* pcq);
//判满
bool CqFull(cq* pcq);
//入队
void CqPush(cq* pcq, CQDataType x);
//出队
void CqPop(cq* pcq);
//获取队头数据
CQDataType CqFront(cq* pcq);
//获取队尾数据
CQDataType CqBack(cq* pcq);
//获取队列有效数据个数
int CqSize(cq* pcq);
//销毁队列
void CqDestroy(cq* pcq);
 
在这里我们实现循环队列是基于顺序表的情况下实现。
我们首先思考一下,我们怎么判断循环队列是空还是满。

4.3 循环队列的声明
#define k 4
typedef int CQDataType;
typedef struct CircularQueue
{
	CQDataType* arr;
	int front;//指向队头
	int rear;//指向队尾的下一个位置
}cq;
 
这里我们需要是一个静态的队列,所以我们用#define来声明一个值。这里arr是指向动态开辟内存的一块空间,front指向循环队列的队头,rear指向循环队列队尾的下一个位置。
4.4 循环队列的初始化
void CqInit(cq* pcq)
{
	assert(pcq);
	//多开辟一个空间,避免出现循环队列假溢出的问题
	CQDataType* tmp = (CQDataType*)malloc(sizeof(CQDataType) * (k + 1));
	if (tmp == NULL)
	{
		perror("malloc");
		exit(1);
	}
	pcq->arr = tmp;
	pcq->front = pcq->rear = 0;
} 
4.5 循环队列判空
bool CqEmpty(cq* pcq)
{
	assert(pcq);
	//头和尾相等表示空
	return pcq->front == pcq->rear;
} 
4.6 循环队列判满
bool CqFull(cq* pcq)
{
	assert(pcq);
	//尾+1再模k+1解决了回绕问题
	return (pcq->rear + 1) % (k + 1) == pcq->front;
} 
4.7 循环队列入队
void CqPush(cq* pcq, CQDataType x)
{
	assert(pcq);
	//判断循环队列是否满了
	assert(!CqFull(pcq));
	pcq->arr[pcq->rear++] = x;
	//解决了循环队列回绕的问题
	pcq->rear %= (k + 1);
} 
4.8 循环队列出队
void CqPop(cq* pcq)
{
	assert(pcq);
	//判断循环队列是否为空
	assert(!CqEmpty(pcq));
	pcq->front++;
	pcq->front %= (k + 1);
} 
4.9 循环队列获取队头数据
CQDataType CqFront(cq* pcq)
{
	assert(pcq);
	assert(!CqEmpty(pcq));
	return pcq->arr[pcq->front];
} 
4.10 循环队列获取队尾数据
这里我们再获取循环队列队尾数据的时候,不是尾-1,当尾在0这个位置的时候,-1就是-1了,下标是不能访问-1的,所以我们这里有两种写法,可以写成pcq->rear == 0 ? 4 : pcq->rear-1,利用三目操作符进行判断。或者先让他-1再模k+1,最后整体在模k+1。

CQDataType CqBack(cq* pcq)
{
	assert(pcq);
	assert(!CqEmpty(pcq));
	return pcq->arr[(pcq->rear - 1+ k+1) % (k + 1)];
}
 
4.11 循环队列获取有效数据个数
这里计算循环队列中有效数据个数也不能直接返回rear,rear也不是代表size,这里当rear在front右边时,需要rear-front,当rear在front在边时,需要((rear-front)+(k+1))%(k+1))。其实rear在front右边时,也可与写成在左边的写法。当值小于k+1的时候,模k+1并没有影响。
int CqSize(cq* pcq)
{
	assert(pcq);
	if (CqEmpty(pcq))
	{
		return 0;
	}
	return ((pcq->rear - pcq->front) + (k + 1)) % (k + 1);
} 
4.12 循环队列销毁
void CqDestroy(cq* pcq)
{
	assert(pcq);
	free(pcq->arr);
	pcq->arr = NULL;
	pcq->front = pcq->rear = 0;
} 
5. 循环队列完整代码
5.1 CircularQueue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#define k 4
typedef int CQDataType;
typedef struct CircularQueue
{
	CQDataType* arr;
	int front;//指向队头
	int rear;//指向队尾的下一个位置
}cq;
//初始化
void CqInit(cq* pcq);
//入队
void CqPush(cq* pcq, CQDataType x);
//出队
void CqPop(cq* pcq);
//判空
bool CqEmpty(cq* pcq);
//判满
bool CqFull(cq* pcq);
//获取队头数据
CQDataType CqFront(cq* pcq);
//获取队尾数据
CQDataType CqBack(cq* pcq);
//获取队列有效数据个数
int CqSize(cq* pcq);
//销毁队列
void CqDestroy(cq* pcq);
 
5.2 CircularQueue.c
#include"CircularQueue.h"
void CqInit(cq* pcq)
{
	assert(pcq);
	//多开辟一个空间,避免出现循环队列假溢出的问题
	CQDataType* tmp = (CQDataType*)malloc(sizeof(CQDataType) * (k + 1));
	if (tmp == NULL)
	{
		perror("malloc");
		exit(1);
	}
	pcq->arr = tmp;
	pcq->front = pcq->rear = 0;
}
void CqPush(cq* pcq, CQDataType x)
{
	assert(pcq);
	//判断循环队列是否满了
	assert(!CqFull(pcq));
	pcq->arr[pcq->rear++] = x;
	//解决了循环队列回绕的问题
	pcq->rear %= (k + 1);
}
bool CqEmpty(cq* pcq)
{
	assert(pcq);
	//头和尾相等表示空
	return pcq->front == pcq->rear;
}
bool CqFull(cq* pcq)
{
	assert(pcq);
	//尾+1再模k+1解决了回绕问题
	return (pcq->rear + 1) % (k + 1) == pcq->front;
}
void CqPop(cq* pcq)
{
	assert(pcq);
	//判断循环队列是否为空
	assert(!CqEmpty(pcq));
	pcq->front++;
	pcq->front %= (k + 1);
}
CQDataType CqFront(cq* pcq)
{
	assert(pcq);
	assert(!CqEmpty(pcq));
	return pcq->arr[pcq->front];
}
CQDataType CqBack(cq* pcq)
{
	assert(pcq);
	assert(!CqEmpty(pcq));
	return pcq->arr[(pcq->rear - 1+ k+1) % (k + 1)];
}
int CqSize(cq* pcq)
{
	assert(pcq);
	if (CqEmpty(pcq))
	{
		return 0;
	}
	return ((pcq->rear - pcq->front) + (k + 1)) % (k + 1);
}
void CqDestroy(cq* pcq)
{
	assert(pcq);
	free(pcq->arr);
	pcq->arr = NULL;
	pcq->front = pcq->rear = 0;
}






