队列(Queue) 是一种数据结构,它遵循”先进先出“(FIFO, First In First Out)的原则。队列中的元素顺序插入和删除,插入操作成为 enqueue,删除操作称为 dequeue 。
队列的基本操作:
- enqueue(item):向队列中添加元素。通常是将元素添加到队列的尾部。
- dequeue():从队列中移除并返回最前面的元素,即先进入队列的元素。
- peek():查看队列最前面的元素,但不删除它。
- isEmpty():检查队列是否为空。
- size():返回队列中的元素个数。
队列的实现:
队列可以通过多种数据结构实现,包括数组、链表等。下面是常见的实现方式:
- 数组实现:
- 在数组实现中,队列有两个指针:一个指向队列头部(front),另一个指向队列尾部(rear)。
- 插入元素时,尾指针移动,删除元素时,头指针移动。
- 如果数组满了,就无法再插入新元素;如果空了,就不能再删除元素。
- 链表实现:
- 链表实现的队列通过两个指针(一个指向头部,一个指向尾部)来管理元素。
- 每次操作都会直接调整头部和尾部的指针,效率较高。
- 没有固定的容量限制,适合处理大量动态数据。
队列的应用场景:
- 任务调度:操作系统中的任务调度就是典型的使用队列的场景,任务按顺序执行,遵循先进先出规则。
- 消息队列:在分布式系统中,消息队列用于传递消息、任务或事件,确保消息的顺序性。
- 缓存管理:队列用于实现缓存替换算法,如最早的页面置换算法。
- 广度优先搜索(BFS):在图的遍历中,广度优先搜索使用队列来存储节点。
- 流量控制:在网络协议或通信中,队列用于控制数据包的流动。
队列的变种:
- 双端队列(Dequeue,双向队列):允许从两端插入和删除元素。双端队列可以像队列一样使用,也可以像栈一样使用。
- 优先队列(Priority Queue):在优先队列中,元素按优先级顺序排列,删除操作总是删除优先级最高的元素,而不仅仅是队列前端的元素。通常使用堆(Heap)来实现优先队列。
队列的时间复杂度:
- enqueue: O(1)
- dequeue: O(1)
- peek: O(1)
- isEmpty: O(1)
- size: O(1)
下面是一个简单的 C++ 队列实现示例,使用 链表 来管理队列。我们将实现基本的队列操作:
enqueue
、dequeue
、peek
、isEmpty
和 size
。
#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;
}
代码说明:
- Node 结构体:表示列表中的一个节点,包含数据(
data
)和指向下一个节点的指针(next
)。 - Queue 类:
front
和rear
分别是队列的头指针和尾指针。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
代码扩展
- 可以根据需要扩展队列类,支持不同类型的元素(使用模板)或不同的队列操作。
- 可以使用数组而不是链表来实现队列,特别是在容量已知且固定时。