题目

编写程序,探究双端队列 std::deque(Double-ended Queue)的分段连续缓冲区(Central Map + Buffer Chunks)底层内存模型。验证其同时支持两端 O(1) 的 push_front 与 push_back 操作,且在扩容时无需像 std::vector 那样搬迁已有元素;同时比对 deque 下标随机访问与 vector 在内存连续度上的本质差异。

输入格式:无需外部输入。

输出格式:输出 deque 前后端交替插入过程与各元素内存地址跨块跳变分析。

数据范围与约定:分段连续内存模型与中控映射表机制。

思路与算法

1. 中控表与分段缓冲区
std::deque 不是一个大连续内存块,而是由若干个固定大小(如 512 字节)的连续缓冲区数组构成,通过一个中央指针数组(Map)进行管理调度。

2. 前后端常数级插入
push_front 和 push_back 只需要在已有 chunk 还有剩余空间时直接写入;当空间用尽时仅需申请一个新的 chunk 并将其指针挂载到中央 Map 的对应端,绝对无需拷贝迁移已有数据!

3. 随机访问开销
deque[i] 的寻址需要两次指针跳转:第一次计算出位于哪个 chunk(i / chunkSize),第二次计算在该 chunk 内的偏移量(i % chunkSize),比 vector 的单次基址加偏移略慢。

完整程序

#include <iostream>
#include <deque>
#include <cstddef>

int main() {
    std::deque<int> dq;

    // 1. 交替向头部和尾部常数级插入
    std::cout << "--- 1. 前后双端 O(1) 插入 ---\n";
    dq.push_back(100);
    dq.push_front(99);
    dq.push_back(101);
    dq.push_front(98);
    dq.push_back(102);
    dq.push_front(97);

    std::cout << "deque 序列结果: ";
    for (int x : dq) std::cout << x << " ";
    std::cout << "\n\n";

    // 2. 观察元素地址连续性(检测分段连续边界)
    std::cout << "--- 2. 检查相邻元素是否物理连续 ---\n";
    bool allContig = true;
    for (size_t i = 1; i < dq.size(); ++i) {
        if ((&dq[i] - &dq[i - 1]) != 1) allContig = false;
    }
    std::cout << "当前 6 个元素相邻指针差是否全为 1: "
              << (allContig ? "是(尚未跨块)" : "否(已跨块)") << "\n";

    std::cout << "\n核心结论:\n";
    std::cout << "1. 双端插入性能极佳,彻底消除 vector.insert(begin(), val) 的 O(N) 元素整体搬迁开销;\n";
    std::cout << "2. 元素在逻辑上完全连续有序,但在物理内存上呈现分段分布。\n";

    return 0;
}

运行示例

输出:

--- 1. 前后双端 O(1) 插入 ---
deque 序列结果: 97 98 99 100 101 102 

--- 2. 检查相邻元素是否物理连续 ---
当前 6 个元素相邻指针差是否全为 1: 否(已跨块)

核心结论:
1. 双端插入性能极佳,彻底消除 vector.insert(begin(), val) 的 O(N) 元素整体搬迁开销;
2. 元素在逻辑上完全连续有序,但在物理内存上呈现分段分布。