在学习数据结构的过程中,**队列(Queue)是继栈之后非常重要的一个结构。队列遵循先进先出(FIFO)**的原则,常常用于任务调度、消息处理等场景。今天我们将一起深入了解队列的基本概念、常见类型(普通队列、循环队列、链式队列)以及它们的实现方法。
一、什么是队列?
队列(Queue)是一种线性数据结构 ,它的特点是先进先出(FIFO) 。
队列的元素总是从队头(front)移除,而从队尾(rear)插入。你可以把队列想象成排队买票,最先排队的人最先买到票。
🧩 队列的形象比喻
假设你和朋友们排队买冰淇淋。你站在队伍的队尾,大家按照顺序排队,队伍中的每个人依次向前,直到最先排队的人先买到冰淇淋。你是队列中的一个元素,等待自己的回合。
- 队头(front):最先排队的人。
- 队尾(rear):最后排队的人。
队列操作简介
- 入队(Enqueue):向队列的队尾插入元素。
- 出队(Dequeue):从队列的队头移除元素。
- 队头元素(Front):获取队头元素,但不移除。
- 队列是否为空(isEmpty):检查队列是否为空。
- 队列是否满(isFull):检查队列是否已满(仅对固定大小的队列有用)。
二、普通队列
📦 普通队列的定义
普通队列 是最基础的队列类型。它使用一个数组 来存储队列的元素,通过两个指针(front 和 rear)来表示队头和队尾的位置。
🌿 普通队列的实现(C++)
cpp
#include <iostream>
using namespace std;
#define MAX_SIZE 5 // 队列最大容量
class Queue {
private:
int arr[MAX_SIZE]; // 队列存储元素的数组
int front; // 队头指针
int rear; // 队尾指针
public:
Queue() {
front = -1; // 初始化队头为-1,表示队列为空
rear = -1; // 初始化队尾为-1
}
// 入队操作
void enqueue(int value) {
if (rear == MAX_SIZE - 1) {
cout << "队列已满,无法入队!" << endl;
return;
}
if (front == -1) front = 0; // 如果队列为空,设置队头为0
arr[++rear] = value; // 队尾指针向后移动,插入元素
cout << "入队:" << value << endl;
}
// 出队操作
void dequeue() {
if (front == -1 || front > rear) {
cout << "队列为空,无法出队!" << endl;
return;
}
cout << "出队:" << arr[front++] << endl; // 队头指针向后移动,移除元素
}
// 获取队头元素
int frontElement() {
if (front == -1 || front > rear) {
cout << "队列为空!" << endl;
return -1;
}
return arr[front]; // 返回队头元素
}
// 检查队列是否为空
bool isEmpty() {
return front == -1 || front > rear;
}
// 打印队列内容
void print() {
if (front == -1 || front > rear) {
cout << "队列为空!" << endl;
return;
}
cout << "队列内容:";
for (int i = front; i <= rear; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
};
💻 使用普通队列的演示(C++ 示例)
cpp
int main() {
Queue q;
q.enqueue(10);
q.enqueue(20);
q.enqueue(30);
q.enqueue(40);
q.enqueue(50);
q.print(); // 输出队列内容
q.dequeue(); // 出队
q.dequeue(); // 出队
q.print(); // 输出队列内容
cout << "队头元素:" << q.frontElement() << endl;
return 0;
}
🧑💻 代码输出:
入队:10
入队:20
入队:30
入队:40
入队:50
队列内容:10 20 30 40 50
出队:10
出队:20
队列内容:30 40 50
队头元素:30
三、循环队列
📦 循环队列的定义
循环队列 是对普通队列的一种改进。普通队列存在一个问题,当队列的元素被移除后,虽然队列的空间被释放了,但是队头指针和队尾指针 可能会一直保持在队列的开头部分,这就导致了空间浪费。
为了避免这个问题,循环队列通过将队尾指针和队头指针做循环利用,实现队列的循环存储。换句话说,当队尾指针到达数组的末尾时,它会重新回到数组的开头。
🌿 循环队列的实现(C++)
cpp
#include <iostream>
using namespace std;
#define MAX_SIZE 5 // 队列最大容量
class CircularQueue {
private:
int arr[MAX_SIZE]; // 队列存储元素的数组
int front; // 队头指针
int rear; // 队尾指针
public:
CircularQueue() {
front = rear = -1; // 初始化队列为空
}
// 入队操作
void enqueue(int value) {
if ((rear + 1) % MAX_SIZE == front) { // 队列已满
cout << "队列已满,无法入队!" << endl;
return;
}
if (front == -1) front = 0; // 如果队列为空,设置队头为0
rear = (rear + 1) % MAX_SIZE; // 循环队尾指针
arr[rear] = value; // 插入元素
cout << "入队:" << value << endl;
}
// 出队操作
void dequeue() {
if (front == -1) {
cout << "队列为空,无法出队!" << endl;
return;
}
cout << "出队:" << arr[front] << endl;
if (front == rear) { // 队列只有一个元素
front = rear = -1;
} else {
front = (front + 1) % MAX_SIZE; // 循环队头指针
}
}
// 获取队头元素
int frontElement() {
if (front == -1) {
cout << "队列为空!" << endl;
return -1;
}
return arr[front]; // 返回队头元素
}
// 检查队列是否为空
bool isEmpty() {
return front == -1;
}
// 打印队列内容
void print() {
if (front == -1) {
cout << "队列为空!" << endl;
return;
}
cout << "队列内容:";
int i = front;
while (i != rear) {
cout << arr[i] << " ";
i = (i + 1) % MAX_SIZE; // 循环遍历
}
cout << arr[rear] << endl; // 打印队尾元素
}
};
💻 使用循环队列的演示(C++ 示例)
cpp
int main() {
CircularQueue q;
q.enqueue(10);
q.enqueue(20);
q.enqueue(30);
q.enqueue(40);
q.enqueue(50);
q.print(); // 输出队列内容
q.dequeue(); // 出队
q.dequeue(); // 出队
q.print(); // 输出队列内容
cout << "队
头元素:" << q.frontElement() << endl;
return 0;
}
### 🧑💻 代码输出:
入队:10
入队:20
入队:30
入队:40
入队:50
队列内容:10 20 30 40 50
出队:10
出队:20
队列内容:30 40 50
队头元素:30
---
## 四、链式队列
### 📦 链式队列的定义
**链式队列**使用**链表**来实现队列。在链式队列中,每个元素(节点)不仅包含数据,还包含指向下一个元素的指针。链式队列的优点是可以动态地扩展,不会受到固定容量的限制。
### 🌿 链式队列的实现(C++)
```cpp
#include <iostream>
using namespace std;
struct Node {
int data;
Node* next;
};
class LinkedQueue {
private:
Node* front; // 队头指针
Node* rear; // 队尾指针
public:
LinkedQueue() {
front = rear = nullptr; // 初始化队列为空
}
// 入队操作
void enqueue(int value) {
Node* newNode = new Node();
newNode->data = value;
newNode->next = nullptr;
if (rear == nullptr) {
front = rear = newNode; // 如果队列为空,头尾指针都指向新节点
} else {
rear->next = newNode; // 将新节点加入队尾
rear = newNode;
}
cout << "入队:" << value << endl;
}
// 出队操作
void dequeue() {
if (front == nullptr) {
cout << "队列为空,无法出队!" << endl;
return;
}
Node* temp = front;
cout << "出队:" << temp->data << endl;
front = front->next; // 队头指针后移
delete temp; // 释放内存
}
// 获取队头元素
int frontElement() {
if (front == nullptr) {
cout << "队列为空!" << endl;
return -1;
}
return front->data; // 返回队头元素
}
// 检查队列是否为空
bool isEmpty() {
return front == nullptr;
}
// 打印队列内容
void print() {
if (front == nullptr) {
cout << "队列为空!" << endl;
return;
}
cout << "队列内容:";
Node* temp = front;
while (temp != nullptr) {
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
};
五、小结
| 队列类型 | 特点 | 优点 | 缺点 |
|---|---|---|---|
| 普通队列 | 使用数组实现,队头和队尾使用指针管理 | 实现简单,适用于固定大小的队列 | 队列满时无法扩展,空间利用率低 |
| 循环队列 | 对普通队列的优化,通过循环利用数组空间 | 更高效的空间利用 | 需要处理队头和队尾指针的循环问题 |
| 链式队列 | 使用链表实现,队列元素动态分配空间 | 动态扩展,适合不确定大小的队列 | 节点管理复杂,增加了内存开销 |