《数据结构笔记六》队列 ⭐⭐⭐

一、队列的基本概念与特性

队列(Queue) 是一种 先进先出(FIFO, First In First Out) 的线性数据结构,核心操作包括:

  • enqueue:元素入队(添加到队尾)

  • dequeue:元素出队(移除队首元素)

  • front:查看队首元素(不移除)

  • isEmpty:判断队列是否为空

  • isFull:判断队列是否已满(仅限固定容量队列)


二、C语言实现队列(基于循环数组)

1. 队列的结构定义
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_SIZE 5  // 队列的最大容量

typedef struct {
    int front;      // 队首指针(指向第一个元素)
    int rear;       // 队尾指针(指向最后一个元素的下一个位置)
    int capacity;   // 队列容量
    int* array;     // 存储元素的数组
} Queue;
2. 核心操作函数
// 初始化队列
Queue* createQueue(int capacity) {
    Queue* queue = (Queue*)malloc(sizeof(Queue));
    queue->capacity = capacity;
    queue->front = 0;
    queue->rear = 0;
    queue->array = (int*)malloc(capacity * sizeof(int));
    return queue;
}
// 判断队列是否为空
bool isEmpty(Queue* queue) {
    return queue->front == queue->rear;
}
// 判断队列是否已满
bool isFull(Queue* queue) {
    return (queue->rear + 1) % queue->capacity == queue->front;
}

// 入队操作
void enqueue(Queue* queue, int item) {
    if (isFull(queue)) {
        printf("Queue is full! Cannot enqueue %d\n", item);
        return;
    }
    queue->array[queue->rear] = item;
    queue->rear = (queue->rear + 1) % queue->capacity;  // 循环移动指针
}

// 出队操作
int dequeue(Queue* queue) {
    if (isEmpty(queue)) {
        printf("Queue is empty!\n");
        return -1;  // 错误码
    }
    int item = queue->array[queue->front];
    queue->front = (queue->front + 1) % queue->capacity;  // 循环移动指针
    return item;
}
// 查看队首元素
int front(Queue* queue) {
    if (isEmpty(queue)) {
        printf("Queue is empty!\n");
        return -1;
    }
    return queue->array[queue->front];
}
// 释放队列内存
void freeQueue(Queue* queue) {
    free(queue->array);
    free(queue);
}
3. 测试用例
int main() {
    Queue* queue = createQueue(MAX_SIZE);
    enqueue(queue, 10);
    enqueue(queue, 20);
    enqueue(queue, 30);
    enqueue(queue, 40);  // 队列满(MAX_SIZE=5时实际可用容量为4,因循环队列需浪费一个位置)
    printf("Front element: %d\n", front(queue));  // 应输出10
    printf("Dequeued: %d\n", dequeue(queue));     // 10出队
    printf("Dequeued: %d\n", dequeue(queue));     // 20出队
    enqueue(queue, 50);  // 入队50
    enqueue(queue, 60);  // 入队60
    printf("Queue is full? %s\n", isFull(queue) ? "Yes" : "No");  // 应输出Yes
    freeQueue(queue);
    return 0;
}

三、深度分析

1. 循环数组设计的必要性
  • 假溢出问题
    普通数组实现队列时,rear指针到达数组末尾后即使前面有空位也无法继续入队(假溢出)。
    循环数组通过取模运算((rear + 1) % capacity)实现指针循环,复用空闲空间。

  • 队列空/满判断

    • 空队列front == rear

    • 满队列(rear + 1) % capacity == front

    • 牺牲一个存储单元:为了区分空和满状态,实际可用容量为 capacity - 1

2. 核心操作的时间复杂度
操作时间复杂度说明
enqueueO(1)直接操作队尾指针
dequeueO(1)直接操作队首指针
frontO(1)直接访问队首元素
isEmptyO(1)检查 front == rear
3. 数组实现 vs 链表实现
特性数组实现链表实现
内存占用固定容量,可能浪费空间动态分配,无容量限制
扩容成本需重新分配内存并复制数据(O(n))动态添加节点(O(1))
访问速度缓存友好,连续内存访问指针跳转,缓存不友好
实现复杂度需处理循环指针逻辑需维护头尾节点指针
4. 应用场景
  • 任务调度:操作系统中的进程就绪队列。

  • 数据缓冲:网络数据包的接收缓冲区。

  • 广度优先搜索(BFS):按层级遍历树或图。

  • 打印机任务队列:按顺序处理打印请求。


四、链表实现队列

typedef struct Node {
    int data;
    struct Node* next;
} Node;

typedef struct {
    Node* front;  // 队首指针
    Node* rear;   // 队尾指针
} LinkedQueue;

// 初始化链表队列
LinkedQueue* createLinkedQueue() {
    LinkedQueue* queue = (LinkedQueue*)malloc(sizeof(LinkedQueue));
    queue->front = queue->rear = NULL;
    return queue;
}

// 入队(链表尾插法)
void linkedEnqueue(LinkedQueue* queue, int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->next = NULL;

    if (queue->rear == NULL) {  // 空队列
        queue->front = queue->rear = newNode;
    } else {
        queue->rear->next = newNode;
        queue->rear = newNode;
    }
}

// 出队
int linkedDequeue(LinkedQueue* queue) {
    if (queue->front == NULL) return -1;
    Node* temp = queue->front;
    int data = temp->data;
    queue->front = queue->front->next;

    if (queue->front == NULL) {  // 队列空后重置rear
        queue->rear = NULL;
    }
    free(temp);
    return data;
}

五、关键设计细节

  1. 循环指针的数学运算

    • 入队时更新 rearrear = (rear + 1) % capacity

    • 出队时更新 frontfront = (front + 1) % capacity

  2. 动态扩容策略(可选)

    • 当队列满时,可分配更大的数组并迁移数据(例如容量翻倍)。

  3. 错误处理

    • 入队时检测队列满,出队时检测队列空。

  4. 内存管理

    • 链表实现需逐个释放节点内存。


六、总结

  • 队列的核心思想:通过限制操作位置(队尾入队、队首出队),强制实现FIFO逻辑。

  • 实现选择

    • 需要快速访问和固定容量时选择数组实现。

    • 需要动态扩容时选择链表实现。

  • 应用本质:队列适用于需要“顺序处理”的场景,如任务调度、数据缓冲等。

通过代码实现和理论分析,可以更深入地理解队列在算法和系统开发中的应用价值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值