队列(Queue)是一种先进先出(FIFO,First-In-First-Out)的线性数据结构。它类似于现实生活中的排队,新元素被添加到队尾,而从队首移除元素。
1. 队列的基本操作:
- Enqueue (入队): 将一个元素添加到队列的尾部。
- Dequeue (出队): 从队列的头部移除一个元素,并返回该元素的值。
- Front (队首): 返回队首元素的值,但不移除它。
- Rear (队尾): 返回队尾元素的值,但不移除它。
- IsEmpty (判空): 检查队列是否为空。
- IsFull (判满): 检查队列是否已满(对于固定大小的队列)。
- Size (大小): 返回队列中元素的数量。
2. 队列的实现方式:
C语言中,队列通常可以通过两种方式实现:
- 数组实现 (顺序队列): 使用数组来存储队列元素,需要维护队首和队尾指针。
- 链表实现 (链式队列): 使用链表来存储队列元素,每个节点包含数据和指向下一个节点的指针。
3. 数组实现 (顺序队列):
3.1 结构体定义:
- #define MAX_SIZE 100 // 定义队列的最大容量
- typedef struct {
- int data[MAX_SIZE]; // 存储队列元素的数组
- int front; // 队首指针,指向队首元素
- int rear; // 队尾指针,指向队尾元素的下一个位置
- } Queue;
复制代码
3.2 初始化队列:
- void initQueue(Queue *q) {
- q->front = 0;
- q->rear = 0;
- }
复制代码
3.3 判空:
- int isEmpty(Queue *q) {
- return q->front == q->rear;
- }
复制代码
3.4 判满:
- int isFull(Queue *q) {
- return (q->rear + 1) % MAX_SIZE == q->front; // 循环队列的判满条件
- }
复制代码
3.5 入队:
- int enqueue(Queue *q, int value) {
- if (isFull(q)) {
- printf("Queue is full!\n");
- return 0; // 入队失败
- }
- q->data[q->rear] = value;
- q->rear = (q->rear + 1) % MAX_SIZE; // 循环队列的队尾指针更新
- return 1; // 入队成功
- }
复制代码
3.6 出队:
- int dequeue(Queue *q, int *value) {
- if (isEmpty(q)) {
- printf("Queue is empty!\n");
- return 0; // 出队失败
- }
- *value = q->data[q->front];
- q->front = (q->front + 1) % MAX_SIZE; // 循环队列的队首指针更新
- return 1; // 出队成功
- }
复制代码
3.7 获取队首元素:
- int front(Queue *q, int *value) {
- if (isEmpty(q)) {
- printf("Queue is empty!\n");
- return 0;
- }
- *value = q->data[q->front];
- return 1;
- }
复制代码
3.8 获取队尾元素:
- int rear(Queue *q, int *value) {
- if (isEmpty(q)) {
- printf("Queue is empty!\n");
- return 0;
- }
- *value = q->data[(q->rear - 1 + MAX_SIZE) % MAX_SIZE]; // 注意循环队列的队尾元素位置
- return 1;
- }
复制代码
3.9 循环队列:
为了避免“假溢出”现象(即队列还有空间,但 rear 指针已经到达数组末尾),通常使用循环队列。循环队列通过取模运算 (%) 来实现队首和队尾指针的循环移动。
4. 链表实现 (链式队列):
4.1 结构体定义:
- typedef struct Node {
- int data;
- struct Node *next;
- } Node;
- typedef struct {
- Node *front;
- Node *rear;
- } Queue;
复制代码
4.2 初始化队列:
- void initQueue(Queue *q) {
- q->front = NULL;
- q->rear = NULL;
- }
复制代码
4.3 判空:
- int isEmpty(Queue *q) {
- return q->front == NULL;
- }
复制代码
4.4 入队:
- int enqueue(Queue *q, int value) {
- Node *newNode = (Node *)malloc(sizeof(Node));
- if (newNode == NULL) {
- printf("Memory allocation failed!\n");
- return 0; // 入队失败
- }
- newNode->data = value;
- newNode->next = NULL;
- if (isEmpty(q)) {
- q->front = newNode;
- q->rear = newNode;
- } else {
- q->rear->next = newNode;
- q->rear = newNode;
- }
- return 1; // 入队成功
- }
复制代码
4.5 出队:
- int dequeue(Queue *q, int *value) {
- if (isEmpty(q)) {
- printf("Queue is empty!\n");
- return 0; // 出队失败
- }
- Node *temp = q->front;
- *value = temp->data;
- q->front = q->front->next;
- if (q->front == NULL) {
- q->rear = NULL; // 如果队列为空,则 rear 也需要置为 NULL
- }
- free(temp);
- return 1; // 出队成功
- }
复制代码
4.6 获取队首元素:
- int front(Queue *q, int *value) {
- if (isEmpty(q)) {
- printf("Queue is empty!\n");
- return 0;
- }
- *value = q->front->data;
- return 1;
- }
复制代码
4.7 获取队尾元素:
- int rear(Queue *q, int *value) {
- if (isEmpty(q)) {
- printf("Queue is empty!\n");
- return 0;
- }
- *value = q->rear->data;
- return 1;
- }
复制代码
5. 完整示例 (链表实现):
- #include <stdio.h>
- #include <stdlib.h>
- // ... (链表实现的结构体定义和函数) ...
- int main() {
- Queue q;
- initQueue(&q);
- enqueue(&q, 10);
- enqueue(&q, 20);
- enqueue(&q, 30);
- int value;
- if (front(&q, &value)) {
- printf("Front element: %d\n", value);
- }
- if (rear(&q, &value)) {
- printf("Rear element: %d\n", value);
- }
- while (!isEmpty(&q)) {
- if (dequeue(&q, &value)) {
- printf("Dequeued element: %d\n", value);
- }
- }
- return 0;
- }
复制代码
6. 数组实现 vs 链表实现:
特性 | 数组实现 (顺序队列) | 链表实现 (链式队列) |
---|
内存分配 | 静态分配 | 动态分配 | 空间利用率 | 可能浪费空间 | 更有效利用空间 | 插入/删除 | 可能需要移动元素 | 效率更高 | 大小限制 | 固定大小 | 大小可变 | 实现复杂度 | 相对简单 | 相对复杂 |
总结:
- 数组实现: 简单,但受限于预定义的大小,可能导致空间浪费或溢出。
- 链表实现: 灵活,可以动态调整大小,空间利用率高,插入和删除操作效率更高,但实现相对复杂,需要处理指针。
选择哪种实现方式取决于具体应用场景的需求。如果队列大小已知且固定,
|