队列(Queue)详解

队列(Queue) 是一种数据结构,它遵循”先进先出“(FIFO, First In First Out)的原则。队列中的元素顺序插入和删除,插入操作成为 enqueue,删除操作称为 dequeue

队列的基本操作:

  1. enqueue(item):向队列中添加元素。通常是将元素添加到队列的尾部。
  2. dequeue():从队列中移除并返回最前面的元素,即先进入队列的元素。
  3. peek():查看队列最前面的元素,但不删除它。
  4. isEmpty():检查队列是否为空。
  5. size():返回队列中的元素个数。

队列的实现:
队列可以通过多种数据结构实现,包括数组、链表等。下面是常见的实现方式:

  1. 数组实现
  • 在数组实现中,队列有两个指针:一个指向队列头部(front),另一个指向队列尾部(rear)。
  • 插入元素时,尾指针移动,删除元素时,头指针移动。
  • 如果数组满了,就无法再插入新元素;如果空了,就不能再删除元素。
  1. 链表实现
  • 链表实现的队列通过两个指针(一个指向头部,一个指向尾部)来管理元素。
  • 每次操作都会直接调整头部和尾部的指针,效率较高。
  • 没有固定的容量限制,适合处理大量动态数据。

队列的应用场景:

  1. 任务调度:操作系统中的任务调度就是典型的使用队列的场景,任务按顺序执行,遵循先进先出规则。
  2. 消息队列:在分布式系统中,消息队列用于传递消息、任务或事件,确保消息的顺序性。
  3. 缓存管理:队列用于实现缓存替换算法,如最早的页面置换算法。
  4. 广度优先搜索(BFS):在图的遍历中,广度优先搜索使用队列来存储节点。
  5. 流量控制:在网络协议或通信中,队列用于控制数据包的流动。

队列的变种:

  1. 双端队列(Dequeue,双向队列):允许从两端插入和删除元素。双端队列可以像队列一样使用,也可以像栈一样使用。
  2. 优先队列(Priority Queue):在优先队列中,元素按优先级顺序排列,删除操作总是删除优先级最高的元素,而不仅仅是队列前端的元素。通常使用堆(Heap)来实现优先队列。

队列的时间复杂度:

  • enqueue: O(1)
  • dequeue: O(1)
  • peek: O(1)
  • isEmpty: O(1)
  • size: O(1)

下面是一个简单的 C++ 队列实现示例,使用 链表 来管理队列。我们将实现基本的队列操作:
enqueuedequeuepeekisEmptysize

#include <iostream>

// 定义队列节点
struct Node {
    
    
    int data;       // 节点的数据
    Node* next;     // 指向下一个节点的指针

    Node(int value) : data(value), next(nullptr) {
    
    }
};

// 定义队列类
class Queue {
    
    
private:
    Node* front;    // 队列的头指针
    Node* rear;     // 队列的尾指针
    int count;      // 队列中元素的数量

public:
    // 构造函数
    Queue() : front(nullptr), rear(nullptr), count(0) {
    
    }

    // 析构函数,释放队列中的所有节点
    ~Queue() {
    
    
        while (!isEmpty()) {
    
    
            dequeue();
        }
    }

    // 检查队列是否为空
    bool isEmpty() {
    
    
        return count == 0;
    }

    // 获取队列中元素的数量
    int size() {
    
    
        return count;
    }

    // 插入元素到队列尾部
    void enqueue(int value) {
    
    
        Node* newNode = new Node(value); // 创建新节点
        if (rear) {
    
                          // 如果队列不为空
            rear->next = newNode;         // 将尾部节点指向新节点
        } else {
    
                              // 如果队列为空
            front = newNode;             // 新节点既是头也是尾
        }
        rear = newNode;                   // 更新尾指针
        count++;                          // 更新队列元素数量
    }

    // 删除队列头部的元素
    int dequeue() {
    
    
        if (isEmpty()) {
    
    
            std::cerr << "Queue is empty!" << std::endl;
            return -1;  // 返回 -1 表示队列为空
        }
        Node* temp = front;        // 暂存头部节点
        int value = front->data;   // 获取头部元素的数据
        front = front->next;       // 更新头指针
        if (front == nullptr) {
    
        // 如果队列变空
            rear = nullptr;        // 更新尾指针
        }
        delete temp;               // 删除原头部节点
        count--;                    // 更新队列元素数量
        return value;               // 返回删除的元素值
    }

    // 查看队列头部元素
    int peek() {
    
    
        if (isEmpty()) {
    
    
            std::cerr << "Queue is empty!" << std::endl;
            return -1;  // 返回 -1 表示队列为空
        }
        return front->data;  // 返回头部元素的数据
    }

    // 打印队列中的所有元素
    void printQueue() {
    
    
        if (isEmpty()) {
    
    
            std::cout << "Queue is empty!" << std::endl;
            return;
        }
        Node* temp = front;
        while (temp) {
    
    
            std::cout << temp->data << " ";
            temp = temp->next;
        }
        std::cout << std::endl;
    }
};

// 测试队列
int main() {
    
    
    Queue q;

    // 插入元素
    q.enqueue(10);
    q.enqueue(20);
    q.enqueue(30);
    q.enqueue(40);
    
    std::cout << "Queue after enqueuing 10, 20, 30, 40: ";
    q.printQueue();

    // 获取队列头部元素
    std::cout << "Peek front element: " << q.peek() << std::endl;

    // 删除元素
    std::cout << "Dequeue: " << q.dequeue() << std::endl;
    std::cout << "Dequeue: " << q.dequeue() << std::endl;

    // 打印队列
    std::cout << "Queue after two dequeues: ";
    q.printQueue();

    // 打印队列大小
    std::cout << "Queue size: " << q.size() << std::endl;

    return 0;
}

代码说明:

  1. Node 结构体:表示列表中的一个节点,包含数据(data)和指向下一个节点的指针(next)。
  2. Queue 类
  • frontrear 分别是队列的头指针和尾指针。
  • count 用于记录队列中的元素个数。
  • enqueue 操作将新节点添加到队列尾部。
  • peek 返回队列头部的元素但不删除它。
  • isEmpty 检查队列是否为空。
  • size 返回队列中元素的个数。
  • printQueue 打印队列中的所有元素。

输出示例

Queue after enqueuing 10, 20, 30, 40: 10 20 30 40 
Peek front element: 10
Dequeue: 10
Dequeue: 20
Queue after two dequeues: 30 40 
Queue size: 2

代码扩展

  • 可以根据需要扩展队列类,支持不同类型的元素(使用模板)或不同的队列操作。
  • 可以使用数组而不是链表来实现队列,特别是在容量已知且固定时。