cpp►STL容器->序列容器->deque

本文详细介绍了双端队列(deque)的概念与特性,对比了deque与vector的区别,包括存储方式、性能特点等。通过实例展示了deque的创建与基本操作。

描述

std::deque
template <class T, class Alloc = allocator<T>> class deque;
deque=double + ended + queue.
双端队列是具有动态大小的序列容器,可以在两端(前端或后端)展开或收缩。

特定的库可能以不同的方式实现deques,通常作为某种形式的动态数组。但在任何情况下,它们都允许通过随机访问迭代器直接访问单个元素,并根据需要通过扩展和收缩容器来自动处理存储。

因此,它们提供了类似于vectors的功能,但是在序列的开头,而不仅仅是在序列的结尾,可以有效地插入和删除元素。但是,与vector不同,deque不能保证将其所有元素存储在相邻的存储位置:通过偏移指向另一个元素的指针来访问deque中的元素会导致未定义的行为。

vectors和deques都提供了一个非常相似的接口,可以用于类似的目的,但在内部,它们的工作方式都完全不同:vector使用单个数组,需要偶尔重新分配以进行增长,deque的元素可以分散在不同的存储块中,容器在内部保存必要的信息,以便在恒定时间内提供对其任何元素的直接访问,并具有统一的顺序接口(通过迭代器)。因此,deque在内部要比vector复杂一点,但这使得它在某些情况下能够更有效地增长,特别是对于非常长的序列,在这种情况下,重新分配变得更昂贵。

对于涉及除开始或结束位置以外的元素的频繁插入或删除操作,deque的性能更差,迭代器和引用的一致性不如list和forward_list。

容器属性(Container properties)
  • Sequence
    序列容器中的元素按严格的线性序列排序。单个元素通过其在此序列中的位置进行访问。
  • Dynamic array
    通常作为动态数组实现,它允许直接访问序列中的任何元素,并在序列的开始或结束时提供相对快速的元素添加/移除。
  • Allocator-aware
    容器使用分配器对象动态处理其存储需求。
// constructing deques
#include <iostream>
#include <deque>
int main() {
    unsigned int i;
    std::deque<int> first;// empty deque of ints
    std::deque<int> second(4, 100);// 4 ints with value 100
    std::deque<int> third(second.begin(), second.end());// iterating through second
    std::deque<int> fourth(third);// a copy of third
    // the iterator constructor can be used to copy arrays:
    int myints[] = { 16,2,77,29 };
    std::deque<int> fifth(myints, myints + sizeof(myints) / sizeof(int));

    std::cout << "The contents of fifth are:";
    for (std::deque<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
        std::cout << ' ' << *it;
    return 0;
}
The contents of fifth are: 16 2 77 29
### deque 容器的实现原理 deque(double-ended queue)是 C++ STL 中的一种序列容器,其设计目标是在两端进行高效的插入和删除操作,并且保持随机访问的能力。与 vector 不同,deque 的内部结构并非单一连续内存块,而是由多个定长的连续内存块组成,通过一个“中控器”来管理这些内存块[^1]。 #### 内部数据结构 deque 的底层实现依赖于一个称为 `_Deque_base` 的类模板,它负责维护一系列缓冲区(buffer),每个缓冲区是一个定长的连续内存块。这些缓冲区的地址被存储在一个指针数组 `_M_map` 中,该数组也被称为“中控器”,用于记录当前所有可用的缓冲区位置[^4]。 - `_M_map` 是一个指向 `_Tp*` 类型的指针数组。 - `_M_map_size` 表示 `_M_map` 数组的大小。 - `_M_start` 和 `_M_finish` 分别表示当前 deque 的起始迭代器和结束迭代器。 当需要扩展 deque 的容量时,如果 `_M_map` 数组不足以容纳新的缓冲区,则会动态分配一个新的更大的 `_M_map`,并将旧的缓冲区地址复制到新的数组中[^4]。 #### 缓冲区大小的确定 STL 使用模板参数 `Bufsize` 来决定每个缓冲区的大小。若 `Bufsize` 为零,则根据元素大小自动调整,确保每个缓冲区至少可以容纳 512 字节。这种机制使得 deque 在不同平台上都能保持良好的性能表现[^4]。 例如,在处理 `int` 类型时,若 `sizeof(int)` 为 4 字节,则每个缓冲区可容纳 512 / 4 = 128 个 `int` 元素。对于较大的对象,如 `string` 或 `vector`,则会相应减少缓冲区内的元素数量以保证总字节数不小于 512。 #### 迭代器的设计 deque 的迭代器是一个复杂的类对象,用于维护当前指向的缓冲区、缓冲区的起始与结束位置,以及指向整个 map 的指针。这种设计允许迭代器跨越多个缓冲区,从而提供随机访问能力[^3]。 具体而言,迭代器包含以下成员: - 指向当前缓冲区的指针; - 当前缓冲区的起始地址; - 当前缓冲区的结束地址; - 指向 `_M_map` 的指针。 通过这种方式,迭代器可以在不同缓冲区间无缝切换,实现对整个 deque 的遍历。 #### 时间复杂度分析 在 deque 的两端执行插入或删除操作的时间复杂度为 O(1),这得益于其双端队列的特性。而在中间插入或删除则需要移动元素,时间复杂度为 O(n)[^3]。 此外,由于 deque 的迭代器复杂性较高,直接对其进行排序可能效率不高。建议先将 deque 内容复制到 vector 中进行排序,然后再复制回去以提高性能[^3]。 #### 示例代码:C++ 中的 deque 基本用法 ```cpp #include <iostream> #include <deque> int main() { std::deque<int> dq; // 向两端添加元素 dq.push_front(1); dq.push_back(2); dq.push_front(0); // 输出 deque 元素 for (const auto& val : dq) { std::cout << val << " "; } std::cout << std::endl; // 移除两端元素 dq.pop_front(); dq.pop_back(); // 再次输出 for (const auto& val : dq) { std::cout << val << " "; } std::cout << std::endl; return 0; } ``` 上述代码演示了如何使用 `push_front` 和 `push_back` 方法在 deque 的两端插入元素,并通过 `pop_front` 和 `pop_back` 方法移除元素。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzyjr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值