hyx0411

导航

队列

队列(Queue)——像排队一样简单

什么是队列?

队列是一种先进先出(FIFO,First In First Out)的数据结构。就像我们在生活中排队买票一样,先来的人先买到票,后来的人排在队尾

在计算机中,队列的应用非常广泛:

  • 操作系统的任务调度
  • 打印机任务排队
  • 消息队列
  • 广度优先搜索(BFS)

队列的基本操作

一个标准的队列通常支持以下操作:

操作 说明
initQueue() 初始化队列
enQueue() 入队,在队尾添加元素
deQueue() 出队,移除队首元素
front() 获取队首元素
rear() 获取队尾元素
isEmpty() 判断队列是否为空
isFull() 判断队列是否已满

队列的两种实现方式

1. 顺序队列(数组实现)

顺序队列使用数组来存储数据,需要两个指针:front 指向队首,rear 指向队尾。

基本代码实现

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int front;  // 队首索引
    int rear;   // 队尾索引
} Queue;

// 初始化队列
void initQueue(Queue *q) {
    q->front = 0;
    q->rear = 0;
}

// 判断队列是否为空
bool isEmpty(Queue *q) {
    return q->front == q->rear;
}

// 判断队列是否已满
bool isFull(Queue *q) {
    return q->rear == MAX_SIZE;
}

// 入队操作
bool enQueue(Queue *q, int value) {
    if (isFull(q)) {
        printf("队列已满,无法入队!\n");
        return false;
    }
    q->data[q->rear] = value;
    q->rear++;
    return true;
}

// 出队操作
bool deQueue(Queue *q) {
    if (isEmpty(q)) {
        printf("队列为空,无法出队!\n");
        return false;
    }
    q->front++;
    return true;
}

// 获取队首元素
int getFront(Queue *q) {
    if (isEmpty(q)) {
        printf("队列为空!\n");
        return -1;
    }
    return q->data[q->front];
}

// 获取队尾元素
int getRear(Queue *q) {
    if (isEmpty(q)) {
        printf("队列为空!\n");
        return -1;
    }
    return q->data[q->rear - 1];
}

// 打印队列所有元素
void printQueue(Queue *q) {
    if (isEmpty(q)) {
        printf("队列为空\n");
        return;
    }
    printf("队列元素:");
    for (int i = q->front; i < q->rear; i++) {
        printf("%d ", q->data[i]);
    }
    printf("\n");
}

// 测试代码
int main() {
    Queue q;
    initQueue(&q);
    
    // 入队
    enQueue(&q, 10);
    enQueue(&q, 20);
    enQueue(&q, 30);
    printQueue(&q);  // 输出:10 20 30
    
    // 获取队首元素
    printf("队首元素:%d\n", getFront(&q));  // 输出:10
    
    // 出队
    deQueue(&q);
    printQueue(&q);  // 输出:20 30
    
    // 获取队尾元素
    printf("队尾元素:%d\n", getRear(&q));   // 输出:30
    
    return 0;
}

顺序队列的问题:假溢出

上面的实现有一个问题:当 rear 到达数组末尾时,即使队列前面还有空闲位置,也无法再入队了。这就是假溢出现象。

解决方案:使用循环队列

2. 循环队列(数组实现)

循环队列将数组看作一个环,当 rear 到达数组末尾时,如果数组前面有空位,就绕回到数组开头。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int front;
    int rear;
} CirQueue;

// 初始化循环队列
void initCirQueue(CirQueue *q) {
    q->front = 0;
    q->rear = 0;
}

// 判断循环队列是否为空
bool isEmpty(CirQueue *q) {
    return q->front == q->rear;
}

// 判断循环队列是否已满
bool isFull(CirQueue *q) {
    return (q->rear + 1) % MAX_SIZE == q->front;
}

// 入队操作
bool enCirQueue(CirQueue *q, int value) {
    if (isFull(q)) {
        printf("队列已满,无法入队!\n");
        return false;
    }
    q->data[q->rear] = value;
    q->rear = (q->rear + 1) % MAX_SIZE;  // 循环移动
    return true;
}

// 出队操作
bool deCirQueue(CirQueue *q) {
    if (isEmpty(q)) {
        printf("队列为空,无法出队!\n");
        return false;
    }
    q->front = (q->front + 1) % MAX_SIZE;  // 循环移动
    return true;
}

// 获取队首元素
int getFront(CirQueue *q) {
    if (isEmpty(q)) {
        printf("队列为空!\n");
        return -1;
    }
    return q->data[q->front];
}

// 打印循环队列
void printCirQueue(CirQueue *q) {
    if (isEmpty(q)) {
        printf("队列为空\n");
        return;
    }
    printf("队列元素:");
    int i = q->front;
    while (i != q->rear) {
        printf("%d ", q->data[i]);
        i = (i + 1) % MAX_SIZE;
    }
    printf("\n");
}

// 测试代码
int main() {
    CirQueue q;
    initCirQueue(&q);
    
    // 入队
    enCirQueue(&q, 1);
    enCirQueue(&q, 2);
    enCirQueue(&q, 3);
    printCirQueue(&q);  // 输出:1 2 3
    
    // 出队
    deCirQueue(&q);
    printCirQueue(&q);  // 输出:2 3
    
    // 继续入队
    enCirQueue(&q, 4);
    enCirQueue(&q, 5);
    printCirQueue(&q);  // 输出:2 3 4 5
    
    return 0;
}

3. 链式队列(链表实现)

链式队列使用链表存储数据,不受数组大小的限制,可以动态扩展。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 节点结构
typedef struct Node {
    int data;
    struct Node *next;
} Node;

// 队列结构
typedef struct {
    Node *front;  // 队首指针
    Node *rear;   // 队尾指针
} LinkQueue;

// 初始化链式队列
void initLinkQueue(LinkQueue *q) {
    q->front = NULL;
    q->rear = NULL;
}

// 判断链式队列是否为空
bool isEmpty(LinkQueue *q) {
    return q->front == NULL;
}

// 入队操作
void enLinkQueue(LinkQueue *q, int value) {
    // 创建新节点
    Node *newNode = (Node *)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("内存分配失败!\n");
        return;
    }
    newNode->data = value;
    newNode->next = NULL;
    
    // 如果队列为空
    if (isEmpty(q)) {
        q->front = newNode;
        q->rear = newNode;
    } else {
        q->rear->next = newNode;
        q->rear = newNode;
    }
    printf("%d 入队成功\n", value);
}

// 出队操作
bool deLinkQueue(LinkQueue *q) {
    if (isEmpty(q)) {
        printf("队列为空,无法出队!\n");
        return false;
    }
    
    Node *temp = q->front;
    int value = temp->data;
    q->front = q->front->next;
    
    // 如果队列只有一个节点
    if (q->front == NULL) {
        q->rear = NULL;
    }
    
    free(temp);
    printf("%d 出队成功\n", value);
    return true;
}

// 获取队首元素
int getFront(LinkQueue *q) {
    if (isEmpty(q)) {
        printf("队列为空!\n");
        return -1;
    }
    return q->front->data;
}

// 打印链式队列
void printLinkQueue(LinkQueue *q) {
    if (isEmpty(q)) {
        printf("队列为空\n");
        return;
    }
    printf("队列元素:");
    Node *cur = q->front;
    while (cur != NULL) {
        printf("%d ", cur->data);
        cur = cur->next;
    }
    printf("\n");
}

// 释放队列内存
void freeQueue(LinkQueue *q) {
    Node *cur = q->front;
    while (cur != NULL) {
        Node *temp = cur;
        cur = cur->next;
        free(temp);
    }
    q->front = NULL;
    q->rear = NULL;
}

// 测试代码
int main() {
    LinkQueue q;
    initLinkQueue(&q);
    
    enLinkQueue(&q, 100);
    enLinkQueue(&q, 200);
    enLinkQueue(&q, 300);
    printLinkQueue(&q);  // 输出:100 200 300
    
    printf("队首元素:%d\n", getFront(&q));  // 输出:100
    
    deLinkQueue(&q);
    printLinkQueue(&q);  // 输出:200 300
    
    // 释放内存
    freeQueue(&q);
    
    return 0;
}

三种实现方式对比

特性 顺序队列 循环队列 链式队列
实现难度 简单 中等 中等
空间利用 有假溢出问题 空间利用率高 动态扩展,无浪费
性能 入出队 O(1) 入出队 O(1) 入出队 O(1)
内存限制 固定大小 固定大小 动态分配
适用场景 数据量小且固定 数据量固定 数据量不确定

队列的常见应用场景

1. 生产者-消费者模型

// 简单的生产者-消费者模型示例
#include <stdio.h>
#include <unistd.h>  // sleep函数

#define BUFFER_SIZE 5

// 假设这里是共享队列
// 生产者不断产生数据入队
// 消费者不断从队列取出数据处理

void producer(Queue *q) {
    int item = 0;
    while (1) {
        if (!isFull(q)) {
            enQueue(q, item++);
            printf("生产者生产了:%d\n", item);
        }
        sleep(1);
    }
}

void consumer(Queue *q) {
    while (1) {
        if (!isEmpty(q)) {
            int item = getFront(q);
            deQueue(q);
            printf("消费者消费了:%d\n", item);
        }
        sleep(1);
    }
}

2. 广度优先搜索(BFS)

在图的遍历中,队列是BFS的核心数据结构:

// BFS伪代码
void BFS(Graph *g, int start) {
    Queue q;
    initQueue(&q);
    visited[start] = true;
    enQueue(&q, start);
    
    while (!isEmpty(&q)) {
        int v = getFront(&q);
        deQueue(&q);
        printf("访问节点:%d\n", v);
        
        for (每个与v相邻的节点w) {
            if (!visited[w]) {
                visited[w] = true;
                enQueue(&q, w);
            }
        }
    }
}

总结

  1. 队列的核心思想:先进先出(FIFO)
  2. 两种实现方式
    • 数组实现:简单高效,但有大小限制
    • 链表实现:动态扩展,更灵活
  3. 循环队列:解决了顺序队列的假溢出问题
  4. 应用广泛:从操作系统到算法设计都能看到队列的身影

掌握队列这个基础数据结构,对学习更复杂的数据结构和算法非常有帮助。希望这篇文章能帮助你理解队列的原理和实现!


posted on 2026-06-28 20:36  芸歆  阅读(4)  评论(0)    收藏  举报