队列
什么是队列
【理解成我们平时的排队,先进先出,与先进后出的栈相反】
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(head)进行删除操作,而在表的后端(tail)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
对比
不同点 | 顺序表 | 链表 |
存储空间上 | 物理上连续 | 逻辑连续,物理不一定连续 |
随机访问 | 可以直接访问任意元素 | 必须从头结点开始寻找 |
任意位置插入或者删除元素 | 要搬移其他元素,效率低 | 只需更改结点的指针指向,效率高 |
插入 | 动态顺序表,空间不够需要扩容 | 没有容量概念,需要就申请,不用就释放 |
场景 | 元素高效存储,并且需要频繁访问 | 需要在任意位置插入或者删除频繁 |
由此表,以本人拙见,运用链表来实现更为方便,原因:
- 队列拥有多少元素不确定,链表可以做到需要就申请,不用就释放,更为方便
- 队列是先进先出,顺序固定,不需要随机访问
队列的实现:
- 定义结构体
//定义队列结构体
typedef struct Queue {
int* data;
int front;//队首
int rear;//队尾
int size;
}Queue;
- 队列的初始化
【让头尾都为空】
//初始化队列
Queue* initQueue(int size) {
Queue* queue = (Queue*)malloc(sizeof(Queue));
queue->data = (int*)malloc(size * sizeof(int));
queue->front = queue->rear = -1; //初始时队列为空
queue->size = size;
return queue;
}
- 判断队列是否为空
//判断是否为空队列
bool isEmpty(Queue* queue) {
return queue->front == -1;// 队首指针为-1表示队列为空
}
- 判断队列是否为满
//判断队列是否满
bool isFull(Queue* queue) {
return (queue->rear + 1) % queue->size == queue->front; // 队列满时队尾指针的下一个位置是队首指针
}
- 入队
我们要开辟新的结点来存储数据
//入队
void enterQueue(Queue* queue, int value) {
if (isFull(queue)) {
printf("队列已满,无法入队。\n");
return;
}
if (isEmpty(queue)) {
queue->front = 0;//队首指针为0
}
queue->rear = (queue->rear + 1) % queue->size;//队尾指针循环增加
queue->data[queue->rear] = value;
}
- 出队
//出队
int outQueue(Queue* queue) {
if (isEmpty(queue)) {
printf("队列为空,无法出队。\n");
return -1;
}
int value = queue->data[queue->front];
if (queue->front == queue->rear){
queue->front = queue->rear = -1;//只有一个元素出队之后,队列为空
}
else
{
queue->front = (queue->front + 1) % queue->size;//队首指针循环增加
}
return value;
}
- 销毁队列
//销毁队列
void destroy(Queue* queue) {
free(queue->data); // 释放队列元素数组内存
free(queue); // 释放队列结构体内存
}
完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 定义队列结构体
typedef struct Queue {
int* data; // 存储队列元素的数组
int front; // 队首指针
int rear; // 队尾指针
int capacity; // 队列容量
} Queue;
// 初始化队列
Queue* initQueue(int capacity) {
Queue* queue = (Queue*)malloc(sizeof(Queue));
queue->data = (int*)malloc(capacity * sizeof(int));
queue->front = queue->rear = -1; // 初始时队列为空
queue->capacity = capacity;
return queue;
}
// 判断队列是否为空
bool isEmpty(Queue* queue) {
return queue->front == -1; // 队首指针为-1表示队列为空
}
// 判断队列是否已满
bool isFull(Queue* queue) {
return (queue->rear + 1) % queue->capacity == queue->front; // 队列满时队尾指针的下一个位置是队首指针
}
// 入队操作
void enqueue(Queue* queue, int value) {
if (isFull(queue)) {
printf("队列已满,无法入队。\n");
return;
}
if (isEmpty(queue)) {
queue->front = 0; // 队列为空时设置队首指针为0
}
queue->rear = (queue->rear + 1) % queue->capacity; // 队尾指针循环增加
queue->data[queue->rear] = value; // 入队操作
}
// 出队操作
int dequeue(Queue* queue) {
if (isEmpty(queue)) {
printf("队列为空,无法出队。\n");
return -1;
}
int value = queue->data[queue->front]; // 获取队首元素
if (queue->front == queue->rear) {
queue->front = queue->rear = -1; // 队列中只有一个元素时出队后队列为空
}
else {
queue->front = (queue->front + 1) % queue->capacity; // 队首指针循环增加
}
return value;
}
// 打印队列元素
void printQueue(Queue* queue) {
if (isEmpty(queue)) {
printf("队列为空,无法打印。\n");
return;
}
printf("队列元素:");
int index = queue->front;
while (index != queue->rear) {
printf("%d ", queue->data[index]);
index = (index + 1) % queue->capacity; // 循环遍历队列
}
printf("%d\n", queue->data[index]); // 打印队尾元素
}
// 销毁队列
void destroyQueue(Queue* queue) {
free(queue->data); // 释放队列元素数组内存
free(queue); // 释放队列结构体内存
}
int main() {
Queue* queue = initQueue(5); // 初始化队列容量为5的队列
enqueue(queue, 3);
enqueue(queue, 9);
enqueue(queue, 5);
printQueue(queue); // 打印队列元素:3 9 5
printf("队首元素:%d\n", dequeue(queue)); // 出队并打印队首元素:3
printQueue(queue); // 打印队列元素:9 5
printf("队首元素:%d\n", dequeue(queue)); // 出队并打印队首元素:9
printf("队首元素:%d\n", dequeue(queue)); // 出队并打印队首元素:5
printf("是否为空:%d\n", isEmpty(queue)); // 打印队列是否为空:1 (空队列为true)
enqueue(queue, 7);
enqueue(queue, 2);
printQueue(queue); // 打印队列元素:7 2
printf("是否为满:%d\n", isFull(queue)); // 打印队列是否为满:0 (未满为false)
destroyQueue(queue); // 销毁队列
return 0;
}
代码效果:
总结:
1. 队列的基本操作:入队、出队、判断队列是否为空 和 判断队列是否已满。
2. 队列的存储方式:可以使用数组(顺序表)或链表来存储队列中的元素。使用数组时,需要考虑队列的大小和元素的类型;使用链表时,需要考虑链表的节点类型和节点的插入和删除操作。【本文使用的是链表】
3. 队列的入队和出队操作:入队操作需要将元素添加到队尾,出队操作需要将队首的元素取出。在入队和出队操作中,需要考虑队列的边界情况,例如队列已满时无法入队,队列已空时无法出队。
4. 队列的效率:在实现队列时,需要考虑队列的效率。例如,使用数组实现队列时,需要考虑数组的扩容问题;使用链表实现队列时,需要考虑链表的查找和删除操作的时间复杂度。