一.队列的定义和特点
队列是一种先进先出(First In First Out)的线性表。它只允许在表的一端进行插入,而在另一端删除数据。允许插入的一端称为队尾(rear),允许删除的一端则称为队头(front)。队列与日常生活中的排队是一样的,先进入队列的元素最早离开。
队列的示意图
通过对队列示意图的观察,发现可以用类似数组的方式来实现队列,定义两个指针,一个指针phead用来表示队头,一个指针ptail用来表示队尾。具体的代码实现如下:
#pragma once
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;
typedef struct QueueNode
{
int val;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);
// 入队列
void QueuePush(Queue* pq, QDataType x);
// 出队列
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
//判空
bool QueueEmpty(Queue* pq);
//队列的数据总数
int QueueSize(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
// 入队列
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->val = x;
newnode->next = NULL;
if (pq->ptail)
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
else
{
pq->phead = pq->ptail = newnode;
}
pq->size++;
}
// 出队列
void QueuePop(Queue* pq)
{
assert(pq);
// 0个节点
assert(pq->phead != NULL);
// 一个节点
// 多个节点
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead != NULL);
return pq->phead->val;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail != NULL);
return pq->ptail->val;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
二.循环队列的实现
循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。
循环队列示意图
可以把该循环队列示意图看成一个首尾相连的数组,当队列满且要插入数据,将Tail指针指向回0下标,这样就形成了一个循环队列。
如果像以往一样,有n个数据开n个空间,那么队空和队满的条件分不清,因为队空和队满都用front==rear表示。因此可以采用n个数据开n+1个空间的方法,让front指针指向队头数据,rear指针指向队尾数据的下一位。这样就可以令front==rrear表示队空,front==(rear+1)%(n+1)表示队满。
代码实现如下:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct {
int* queue;
int front;
int rear;
int n;
} MyCircularQueue;
//创建一个可以存放n个元素的循环队列,实际申请的空间为n + 1
MyCircularQueue* myCircularQueueCreate(int n) {
MyCircularQueue* pcq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
pcq->queue = (int*)malloc(sizeof(int) * (n + 1));
pcq->front = 0;
pcq->rear = 0;
pcq->n = n;
return pcq;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//判满
if ((obj->rear + 1) % (obj->n + 1) == obj->front)
{
return false;
}
//队尾入队
obj->queue[obj->rear++] = value;
//如果队尾越界,更新为最小值
if (obj->rear == obj->n + 1)
obj->rear = 0;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
//判空
if (obj->front == obj->rear)
return false;
//队头出队
++obj->front;
//如果队头越界,更新为最小值
if (obj->front == obj->n + 1)
obj->front = 0;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if (obj->front == obj->rear)
return -1;
else
return obj->queue[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if (obj->front == obj->rear)
return -1;
//队尾元素再rear索引的前一个位置,如果rear为0,
//则队尾元素在数组的最后一个位置
if (obj->rear == 0)
return obj->queue[obj->n];
else
return obj->queue[obj->rear - 1];
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->rear + 1) % (obj->n + 1) == obj->front;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->queue);
free(obj);
}
上述是我个人的理解,如有错误,望各位帮忙指正。