栈与队列¶
📖 章节简介¶
本章将介绍C++的栈和队列数据结构,包括栈的实现、队列的实现和循环队列。
上图直观展示了栈的 LIFO 与队列的 FIFO 行为,以及 push/pop、enqueue/dequeue 的操作方向。
📚 栈¶
1. 栈的实现¶
C++
// 栈的实现
#include <iostream>
#include <vector>
using namespace std;
class Stack {
private:
vector<int> data;
int top;
public:
Stack(int size = 10) {
data.resize(size);
top = -1;
}
void push(int value) {
if (top >= (int)data.size() - 1) {
cout << "栈已满!" << endl;
return;
}
data[++top] = value;
}
// ⚠️ 注意:空栈时返回 -1 作为哨兵值存在缺陷——当 -1 是合法数据时会产生歧义。
// 更健壮的做法:抛出异常 (throw std::underflow_error("stack is empty"))
// 或使用 std::optional<int> 作为返回类型(C++17)。
int pop() {
if (top < 0) {
cout << "栈为空!" << endl;
return -1;
}
return data[top--];
}
int peek() const {
if (top < 0) {
cout << "栈为空!" << endl;
return -1;
}
return data[top];
}
bool isEmpty() const {
return top == -1;
}
bool isFull() const {
return top >= (int)data.size() - 1;
}
int size() const {
return top + 1;
}
};
int main() {
Stack stack(5);
// 入栈
stack.push(10);
stack.push(20);
stack.push(30);
cout << "栈大小: " << stack.size() << endl;
// 查看栈顶元素
cout << "栈顶元素: " << stack.peek() << endl;
// 出栈
while (!stack.isEmpty()) {
cout << "出栈: " << stack.pop() << endl;
}
return 0;
}
2. 栈的应用¶
C++
// 栈的应用:括号匹配
#include <iostream>
#include <stack>
using namespace std;
// 括号匹配算法:左括号入栈,遇右括号时弹栈检查是否配对
bool isValidExpression(const string& expr) {
stack<char> s;
for (char c : expr) {
if (c == '(' || c == '[' || c == '{') {
s.push(c); // 左括号入栈
} else if (c == ')' || c == ']' || c == '}') {
if (s.empty()) {
return false; // 没有对应的左括号
}
char top = s.top();
s.pop();
// 检查左右括号是否匹配
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
return false;
}
}
}
return s.empty(); // 栈为空说明所有括号都已配对
}
int main() {
string expr1 = "{[()()]}";
string expr2 = "{[(])}";
cout << "表达式1: " << expr1 << endl;
cout << "是否有效: " << (isValidExpression(expr1) ? "是" : "否") << endl;
cout << "\n表达式2: " << expr2 << endl;
cout << "是否有效: " << (isValidExpression(expr2) ? "是" : "否") << endl;
return 0;
}
📋 队列¶
1. 队列的实现¶
C++
// 队列的实现
#include <iostream>
#include <vector>
using namespace std;
class Queue {
private:
vector<int> data;
int front;
int rear;
int count;
int maxSize;
public:
Queue(int size = 10) {
data.resize(size);
front = 0;
rear = -1;
count = 0;
maxSize = size;
}
void enqueue(int value) {
if (isFull()) {
cout << "队列已满!" << endl;
return;
}
rear = (rear + 1) % maxSize; // 取模实现环形回绕
data[rear] = value;
count++;
}
int dequeue() {
if (isEmpty()) {
cout << "队列为空!" << endl;
return -1;
}
int value = data[front];
front = (front + 1) % maxSize; // front也取模回绕
count--;
return value;
}
int peek() const {
if (isEmpty()) {
cout << "队列为空!" << endl;
return -1;
}
return data[front];
}
bool isEmpty() const {
return count == 0;
}
bool isFull() const {
return count == maxSize;
}
int size() const {
return count;
}
};
int main() {
Queue queue(5);
// 入队
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
cout << "队列大小: " << queue.size() << endl;
// 查看队首元素
cout << "队首元素: " << queue.peek() << endl;
// 出队
while (!queue.isEmpty()) {
cout << "出队: " << queue.dequeue() << endl;
}
return 0;
}
2. 队列的应用¶
C++
// 队列的应用:任务调度
#include <iostream>
#include <queue>
using namespace std;
struct Task {
string name;
int priority;
Task(string n, int p) : name(n), priority(p) {}
};
class TaskScheduler {
private:
queue<Task> tasks;
public:
void addTask(const Task& task) {
tasks.push(task);
cout << "任务添加: " << task.name << endl;
}
Task getNextTask() {
if (tasks.empty()) {
cout << "没有待处理任务" << endl;
return Task("", 0);
}
Task task = tasks.front();
tasks.pop();
return task;
}
bool hasTasks() const {
return !tasks.empty();
}
int getTaskCount() const {
return tasks.size();
}
};
int main() {
TaskScheduler scheduler;
// 添加任务
scheduler.addTask(Task("任务1", 1));
scheduler.addTask(Task("任务2", 2));
scheduler.addTask(Task("任务3", 3));
cout << "任务总数: " << scheduler.getTaskCount() << endl;
// 处理任务
while (scheduler.hasTasks()) {
Task task = scheduler.getNextTask();
cout << "执行任务: " << task.name
<< ", 优先级: " << task.priority << endl;
}
return 0;
}
🔄 循环队列¶
1. 循环队列的实现¶
C++
// 循环队列
#include <iostream>
using namespace std;
// ⚠️ 注意:本类违反了「三/五法则(Rule of Three/Five)」!
// 类中有裸指针 data 和手动 delete[],但未定义拷贝构造函数和拷贝赋值运算符。
// 若发生拷贝(如传值、赋值),两个对象会共享同一块内存,析构时会 double-free。
// 修复方案:① 删除拷贝操作(如下所示);② 或正确实现深拷贝;③ 或改用 std::vector。
class CircularQueue {
private:
int* data;
int front;
int rear;
int count;
int maxSize;
public:
CircularQueue(const CircularQueue&) = delete; // 禁止拷贝
CircularQueue& operator=(const CircularQueue&) = delete; // 禁止拷贝赋值
CircularQueue(int size) {
maxSize = size;
data = new int[maxSize];
front = 0;
rear = -1;
count = 0;
}
~CircularQueue() {
delete[] data;
}
void enqueue(int value) {
if (isFull()) {
cout << "队列已满!" << endl;
return;
}
rear = (rear + 1) % maxSize; // 取模实现环形回绕,到达末尾后回到数组起点
data[rear] = value;
count++;
}
int dequeue() {
if (isEmpty()) {
cout << "队列为空!" << endl;
return -1;
}
int value = data[front];
front = (front + 1) % maxSize; // front同样取模回绕,保持环形结构
count--;
return value;
}
int peek() const {
if (isEmpty()) {
cout << "队列为空!" << endl;
return -1;
}
return data[front];
}
bool isEmpty() const {
return count == 0;
}
bool isFull() const {
return count == maxSize;
}
int size() const {
return count;
}
};
int main() {
CircularQueue queue(5);
// 入队
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
queue.enqueue(40);
queue.enqueue(50);
cout << "队列大小: " << queue.size() << endl;
// 出队
queue.dequeue();
queue.enqueue(60); // 可以继续入队
cout << "队列大小: " << queue.size() << endl;
return 0;
}
💡 最佳实践¶
1. 内存管理¶
C++
// 内存管理
#include <iostream>
using namespace std;
int main() {
// ✅ 好的做法:使用RAII
class Resource {
private:
int* data;
public:
Resource(int size) {
data = new int[size];
cout << "资源分配" << endl;
}
~Resource() {
delete[] data;
cout << "资源释放" << endl;
}
};
{
Resource res(100);
// 自动释放
}
// ❌ 不好的做法:忘记释放内存
int* ptr = new int[100];
// delete[] ptr; // 容易忘记
return 0;
}
2. 异常处理¶
C++
// 异常处理
#include <iostream>
#include <new>
using namespace std;
int main() {
// ✅ 好的做法:处理异常
try {
int* arr = new int[1000000000];
delete[] arr;
} catch (const bad_alloc& e) {
cout << "内存分配失败" << endl;
}
// ❌ 不好的做法:不处理异常
// int* arr = new int[1000000000];
// delete[] arr;
return 0;
}
📝 练习题¶
基础题¶
- 栈和队列有什么区别?
- 如何实现栈和队列?
- 栈和队列有哪些应用场景?
进阶题¶
- 实现循环队列。
- 使用栈实现表达式求值。
- 使用队列实现任务调度。
实践题¶
- 创建一个简单的浏览器历史记录。
- 实现一个打印队列系统。
- 构建一个任务调度器。
📚 推荐阅读¶
🔗 下一章¶
树 - 学习C++的树数据结构。