目录
0.前言
1.容器适配器
1.1容器适配器的特点
1.2容器适配器的实现
1.3使用容器适配器的场景
2.stack的介绍与使用
2.1介绍
2.2使用
3.queue的介绍与使用
3.1介绍
3.2使用
4.stack和queue的模拟实现
4.1 stack的模拟实现
4.2 queue的模拟实现
5.结语
(图像由AI生成)
0.前言
在前面的博客中,我们介绍了STL(Standard Template Library)中的string
、vector
和list
,这些都是作为“容器”而存在的数据结构。它们各有特点和适用场景,并且都属于独立的容器类型。接下来要介绍的stack
和queue
,则是“容器适配器”。它们并不是独立的容器,而是通过某些容器来实现特定的行为和接口。因此,它们被称为“容器适配器”。接下来,就让我们一探究竟。
1.容器适配器
容器适配器(Container Adapters)是C++标准模板库(STL)中的一种特殊类别。与常规容器(如vector
、list
、deque
等)不同,容器适配器不直接管理存储元素的内存,而是基于现有的容器提供一种特定的接口和行为。容器适配器通过限制和修改底层容器的操作来实现其功能。
C++中主要的容器适配器有三种:stack
、queue
和priority_queue
。它们分别适用于不同的场景:
stack
:实现后进先出(LIFO,Last In First Out)数据结构。queue
:实现先进先出(FIFO,First In First Out)数据结构。priority_queue
:实现元素按优先级排序的队列,允许最高优先级的元素先出队。
1.1容器适配器的特点
- 基于现有容器:容器适配器是基于已有容器(如
deque
、list
、vector
)实现的。这意味着它们依赖于这些底层容器的存储和操作机制。 - 限制接口:容器适配器通过限制底层容器的操作接口来实现特定的数据结构行为。例如,
stack
只允许访问栈顶元素,不允许随机访问其他元素。 - 一致性接口:所有容器适配器都提供一致的接口,这使得它们在使用上更加简洁和统一。
1.2容器适配器的实现
容器适配器通过组合现有容器类,并对其进行包装,实现特定的接口。以下是一个自定义的容器适配器MyAdapter
的简化实现:
#include <iostream>
#include <deque>
template <typename T, typename Container = std::deque<T>>
class MyAdapter {
private:
Container c;
public:
// 类型定义
typedef typename Container::value_type value_type;
typedef typename Container::size_type size_type;
typedef typename Container::reference reference;
typedef typename Container::const_reference const_reference;
// 构造函数
explicit MyAdapter(const Container& cont = Container()) : c(cont) {}
// 成员函数
bool isEmpty() const { return c.empty(); }
size_type getSize() const { return c.size(); }
reference getFirst() { return c.front(); }
const_reference getFirst() const { return c.front(); }
void add(const value_type& value) { c.push_back(value); }
void removeFirst() { c.pop_front(); }
};
int main() {
MyAdapter<int> adapter;
adapter.add(1);
adapter.add(2);
adapter.add(3);
std::cout << "First element: " << adapter.getFirst() << std::endl;
adapter.removeFirst();
std::cout << "First element after removal: " << adapter.getFirst() << std::endl;
return 0;
}
在这个简化的实现中,MyAdapter
类是一个模板类,它基于Container
(默认为deque
)实现。通过限制Container
的操作接口,实现了特定的行为:
isEmpty()
:检查容器是否为空。getSize()
:获取容器中的元素数量。getFirst()
:访问容器中的第一个元素。add()
:向容器中添加元素。removeFirst()
:移除容器中的第一个元素。
1.3使用容器适配器的场景
容器适配器在实际开发中有广泛的应用场景。例如:
- 递归和回溯算法:在递归和回溯算法中,
stack
常用于保存临时状态和返回地址。 - 任务调度:
queue
常用于实现任务调度,按顺序处理任务。 - 优先级调度:
priority_queue
常用于实现优先级调度,将高优先级任务优先处理。
通过使用容器适配器,可以简化代码的实现,提高开发效率。同时,由于容器适配器基于已有容器实现,性能和稳定性也得到了保证。
2.stack的介绍与使用
2.1介绍
stack
是一种后进先出(LIFO,Last In First Out)的数据结构。它的特点是最后添加的元素最先被移除。stack
适用于许多计算机科学领域和算法,包括递归、表达式求值、括号匹配、深度优先搜索(DFS)等。C++中的stack
是通过容器适配器实现的,通常基于deque
或vector
容器。
stack
提供了一组特定的接口,包括:
push
:将元素添加到栈顶。pop
:移除栈顶元素。top
:访问栈顶元素,但不移除它。empty
:检查栈是否为空。size
:获取栈中的元素数量。
这些接口使得stack
可以方便地用于实现各种需要后进先出行为的算法和数据处理任务。
与直接使用deque
或vector
不同,stack
通过限制操作接口,确保了只能通过特定的方式访问和修改数据,从而保证了数据的后进先出顺序。这种封装和接口限制提高了代码的安全性和可读性,避免了不必要的操作干扰stack
的逻辑。
2.2使用
stack
容器适配器提供了一组简单但强大的操作接口,使得它能够高效地实现后进先出的数据管理。以下是stack
的主要功能:
push
:将元素添加到栈顶。每次调用push
,新元素都会被放置在栈的顶部。pop
:移除栈顶元素。调用pop
会删除位于栈顶的元素。注意,pop
操作不会返回被移除的元素。top
:访问栈顶元素,但不移除它。top
操作返回当前位于栈顶的元素,但不会改变栈的状态。empty
:检查栈是否为空。如果栈中没有元素,empty
返回true
,否则返回false
。size
:获取栈中的元素数量。size
操作返回栈中当前元素的个数。
以下是一个使用stack
的完整示例代码,展示了这些操作的实际使用:
#include <iostream>
#include <stack>
int main() {
std::stack<int> s;
// 将元素添加到栈顶
s.push(1);
s.push(2);
s.push(3);
// 打印栈的大小
std::cout << "Stack size: " << s.size() << std::endl;
// 访问栈顶元素
std::cout << "Top element: " << s.top() << std::endl;
// 移除栈顶元素
s.pop();
// 再次访问栈顶元素
std::cout << "Top element after pop: " << s.top() << std::endl;
// 打印栈是否为空
std::cout << "Is stack empty? " << (s.empty() ? "Yes" : "No") << std::endl;
// 再次移除栈顶元素并打印状态
s.pop();
s.pop();
std::cout << "Is stack empty after popping all elements? " << (s.empty() ? "Yes" : "No") << std::endl;
return 0;
}
下面是以上代码的运行结果:
Stack size: 3
Top element: 3
Top element after pop: 2
Is stack empty? No
Is stack empty after popping all elements? Yes
3.queue的介绍与使用
3.1介绍
queue
是一种先进先出(FIFO,First In First Out)的数据结构。它的特点是最早添加的元素最先被移除。queue
在很多应用场景中都非常实用,比如任务调度、缓冲区管理、广度优先搜索(BFS)等。C++中的queue
是通过容器适配器实现的,通常基于deque
或list
容器。
queue
提供了一组特定的接口,包括:
push
:将元素添加到队列的末尾。pop
:移除队列的第一个元素。front
:访问队列的第一个元素,但不移除它。back
:访问队列的最后一个元素,但不移除它。empty
:检查队列是否为空。size
:获取队列中的元素数量。
这些接口使得queue
可以方便地用于实现各种需要先进先出行为的算法和数据处理任务。
与直接使用deque
或list
不同,queue
通过限制操作接口,确保了只能通过特定的方式访问和修改数据,从而保证了数据的先进先出顺序。这种封装和接口限制提高了代码的安全性和可读性,避免了不必要的操作干扰queue
的逻辑。
3.2使用
queue
容器适配器提供了一组简洁而强大的操作接口,使得它能够高效地实现先进先出的数据管理。以下是queue
的主要功能:
push
:将元素添加到队列的末尾。每次调用push
,新元素都会被放置在队列的尾部。pop
:移除队列的第一个元素。调用pop
会删除位于队列前端的元素。注意,pop
操作不会返回被移除的元素。front
:访问队列的第一个元素,但不移除它。front
操作返回当前位于队列前端的元素,但不会改变队列的状态。back
:访问队列的最后一个元素,但不移除它。back
操作返回当前位于队列尾部的元素,但不会改变队列的状态。empty
:检查队列是否为空。如果队列中没有元素,empty
返回true
,否则返回false
。size
:获取队列中的元素数量。size
操作返回队列中当前元素的个数。
以下是一个使用queue
的完整示例代码,展示了这些操作的实际使用:
#include <iostream>
#include <queue>
int main() {
std::queue<int> q;
// 将元素添加到队列末尾
q.push(1);
q.push(2);
q.push(3);
// 打印队列的大小
std::cout << "Queue size: " << q.size() << std::endl;
// 访问队列的第一个元素
std::cout << "Front element: " << q.front() << std::endl;
// 访问队列的最后一个元素
std::cout << "Back element: " << q.back() << std::endl;
// 移除队列的第一个元素
q.pop();
// 再次访问队列的第一个元素
std::cout << "Front element after pop: " << q.front() << std::endl;
// 打印队列是否为空
std::cout << "Is queue empty? " << (q.empty() ? "Yes" : "No") << std::endl;
// 再次移除队列的第一个元素并打印状态
q.pop();
q.pop();
std::cout << "Is queue empty after popping all elements? " << (q.empty() ? "Yes" : "No") << std::endl;
return 0;
}
下面是以上代码的运行结果:
Queue size: 3
Front element: 1
Back element: 3
Front element after pop: 2
Is queue empty? No
Is queue empty after popping all elements? Yes
4.stack和queue的模拟实现
4.1 stack的模拟实现
下面是Stack
类的模拟实现,它是一个模板类,使用一个底层容器Container
(默认为deque
)来实现栈的功能。我们将详细介绍其原理,并给出完整的代码和注释。
原理介绍
Stack
类是一个容器适配器,它封装了一个底层容器Container
,并通过限制其接口实现栈的后进先出(LIFO)行为。该类提供了基本的栈操作,包括push
、pop
、top
、size
和empty
。
- push:将元素添加到栈顶。调用
Container
的push_back
方法实现。 - pop:移除栈顶元素。调用
Container
的pop_back
方法实现。 - top:返回栈顶元素。调用
Container
的back
方法实现。 - size:返回栈中元素的数量。调用
Container
的size
方法实现。 - empty:检查栈是否为空。调用
Container
的empty
方法实现。
以下是Stack
类的完整代码和注释:
#pragma once
#include <deque>
#include <list>
using namespace std;
// 模板类Stack,默认使用deque作为底层容器
template<class T, class Container = deque<T>>
class Stack {
private:
// 底层容器,用于存储栈的元素
Container con;
public:
// 默认构造函数
Stack() {}
// 将元素添加到栈顶
void push(const T& val) {
// 使用底层容器的push_back方法将元素添加到末尾
con.push_back(val);
}
// 移除栈顶元素
void pop() {
// 使用底层容器的pop_back方法移除末尾元素
con.pop_back();
}
// 返回栈顶元素
T& top() {
// 使用底层容器的back方法返回末尾元素
return con.back();
}
// 返回栈中元素的数量
size_t size() {
// 使用底层容器的size方法获取元素数量
return con.size();
}
// 检查栈是否为空
bool empty() {
// 使用底层容器的empty方法检查是否为空
return con.empty();
}
};
4.2 queue的模拟实现
下面是Queue
类的模拟实现,它是一个模板类,使用一个底层容器Container
(默认为deque
)来实现队列的功能。我们将详细介绍其原理,并给出完整的代码和注释。
原理介绍
Queue
类是一个容器适配器,它封装了一个底层容器Container
,并通过限制其接口实现队列的先进先出(FIFO)行为。该类提供了基本的队列操作,包括push
、pop
、front
、back
、size
和empty
。
- push:将元素添加到队列的末尾。调用
Container
的push_back
方法实现。 - pop:移除队列的第一个元素。调用
Container
的pop_front
方法实现。 - front:返回队列的第一个元素。调用
Container
的front
方法实现。 - back:返回队列的最后一个元素。调用
Container
的back
方法实现。 - size:返回队列中元素的数量。调用
Container
的size
方法实现。 - empty:检查队列是否为空。调用
Container
的empty
方法实现。
以下是Queue
类的完整代码和注释:
#pragma once
#include <list>
#include <deque>
using namespace std;
// 模板类Queue,默认使用deque作为底层容器
template<class T, class Container = deque<T>>
class Queue {
private:
// 底层容器,用于存储队列的元素
Container con;
public:
// 默认构造函数
Queue() {}
// 将元素添加到队列末尾
void push(const T& val) {
// 使用底层容器的push_back方法将元素添加到末尾
con.push_back(val);
}
// 移除队列的第一个元素
void pop() {
// 使用底层容器的pop_front方法移除第一个元素
con.pop_front();
}
// 返回队列的第一个元素
T& front() {
// 使用底层容器的front方法返回第一个元素
return con.front();
}
// 返回队列的最后一个元素
T& back() {
// 使用底层容器的back方法返回最后一个元素
return con.back();
}
// 返回队列中元素的数量
size_t size() {
// 使用底层容器的size方法获取元素数量
return con.size();
}
// 检查队列是否为空
bool empty() {
// 使用底层容器的empty方法检查是否为空
return con.empty();
}
};
5.结语
通过对C++标准模板库中的stack
和queue
的介绍与使用,我们深入了解了这两种重要的容器适配器。stack
和queue
在许多计算机科学领域和实际应用中都有广泛的使用场景。我们不仅学习了它们的基本操作和应用,还通过模拟实现进一步理解了其内部机制。掌握这些数据结构和容器适配器,将显著提升我们在算法设计和程序开发中的效率和能力。希望这篇博客能帮助读者更好地理解和应用stack
和queue
,为大家在编程道路上提供有力的支持。