1. 双端队列 (Deque) 概述
双端队列(Double-Ended Queue,简称 deque)是一种允许在其两端进行高效插入和删除操作的数据结构。与普通队列(只允许在一端插入和另一端删除)相比,双端队列更为灵活。
C++ 标准库中已经提供了 std::deque
,但通过自行实现一个双端队列,可以更好地理解其内部机制和迭代器的工作原理。
2. 实现思路
为了实现一个高效的双端队列,我们需要考虑以下几点:
- 动态数组:使用动态数组(如环形缓冲区)来存储元素,以便支持在两端进行常数时间的插入和删除。
- 头尾指针:维护头部和尾部的索引,以便快速访问两端。
- 自动扩展:当容量不足时,自动调整内部缓冲区的大小。
- 迭代器支持:定义一个迭代器类,允许用户使用像
begin()
和end()
这样的函数进行遍历。
接下来,我们将一步步实现这些功能。
3. 详细实现
3.1 内部数据结构
我们将使用一个动态分配的数组作为内部缓冲区,并通过头尾索引来管理队列的前后端。为了支持在两端高效插入和删除,我们将采用环形缓冲区的概念,即当索引达到数组的末端时,自动回绕到数组的开头。
3.2 Deque 类
下面是 Deque
类的基本结构和关键成员:
1 |
|
3.3 迭代器类
在上面的 Deque
类中,我们定义了一个嵌套的 Iterator
类。这个迭代器支持前向和后向遍历,并且可以与标准的 C++ 迭代器兼容。
关键点解释:
- 成员变量:
deque_ptr
:指向包含此迭代器的Deque
实例。pos
:相对于队列头部的位置。
- 重载运算符:
operator*
和operator->
:用于访问当前元素。operator++
和operator--
:前置和后置递增和递减,用于移动迭代器。operator==
和operator!=
:用于比较两个迭代器是否相同。
- 注意事项:
- 迭代器并不管理元素的生命周期,只是提供遍历接口。
- 迭代器的有效性依赖于队列的修改操作(如插入和删除)。在实际应用中,需要注意迭代器失效的问题。
4. 使用示例
下面是一个使用上述 Deque
类及其迭代器的示例程序:
1 |
|
预期输出
1 | Deque 大小: 5 |
解释:
- 插入操作:
- 使用
push_back
在队列的后端插入 “Apple”, “Banana”, “Cherry”。 - 使用
push_front
在队列的前端插入 “Date”, “Elderberry”。 - 最终队列顺序为:Elderberry, Date, Apple, Banana, Cherry
- 使用
- 遍历操作:
- 使用迭代器从
begin()
到end()
遍历并打印所有元素。
- 使用迭代器从
- 访问元素:
- 使用
front()
获取队列前端的元素。 - 使用
back()
获取队列后端的元素。
- 使用
- 删除操作:
- 使用
pop_front
删除前端元素(”Elderberry”)。 - 使用
pop_back
删除后端元素(”Cherry”)。 - 删除后,队列顺序为:Date, Apple, Banana
- 使用
5. 完整代码
以下是完整的 Deque
类及其使用示例代码:
1 |
|
编译和运行
保存上述代码到一个文件,例如 DequeWithIterator.cpp
,然后使用 C++ 编译器进行编译和运行:
1 | g++ -std=c++11 -o DequeWithIterator DequeWithIterator.cpp |
预期输出
1 | Deque 大小: 5 |
6. 总结
通过上述步骤,我们成功实现了一个支持双端插入和删除的双端队列(deque),并添加了迭代器支持,使其能够与标准的 C++ 迭代器接口兼容。这个实现包含了以下关键点:
- 内部缓冲区管理:
- 使用动态数组并采用环形缓冲区的方式,支持高效的双端操作。
- 自动调整缓冲区的容量,确保在元素数量增加时仍能保持高效。
- 迭代器实现:
- 定义了一个嵌套的
Iterator
类,支持前向和后向遍历。 - 重载了必要的运算符(如
*
,->
,++
,--
,==
,!=
),以实现与标准迭代器的兼容。
- 定义了一个嵌套的
- 基本操作:
push_front
和push_back
:分别在队列的前端和后端插入元素。pop_front
和pop_back
:分别从队列的前端和后端删除元素。front
和back
:访问队列的前端和后端元素。