C++ STL -5 | deque


一、deque 概述

deque(double-ended queue,双端队列)是 STL 容器中非常重要的一员,支持在容器头尾两端进行高效插入和删除操作,同时也支持随机访问。它经常作为队列、滑动窗口等场景的底层实现。


二、deque 与其他容器的对比

1. 与 vector 的区别

  • 底层结构

    • vector 使用一块连续动态数组存储所有元素,类似 C 语言数组。
    • deque 采用分段连续数组(分块数组),由多个小块(block)组成,通过指针数组(map)统一管理。
  • 访问与操作

    • vector 只适合尾部插入/删除高效,头部/中间效率低。
    • deque 头尾两端插入/删除均高效(O(1) 均摊),中间插入/删除效率也较低(O(n))。
    • vector 下标随机访问最快,deque 支持下标访问但略慢于 vector

2. 与 list 的区别

  • 底层结构

    • list 是双向链表,每个节点分开分配,节点之间通过 next/prev 指针直接相连,无指针数组(map)统一管理。
    • deque 由多个内存块顺序拼接管理,每块内元素连续,块之间可能分散,但通过 map 管理。
  • 操作特性

    • list 任意位置插入/删除都是 O(1)(找到位置后),但随机访问性能极差(O(n))。
    • deque 只在头尾插入/删除高效,支持 O(1) 随机访问。

三、deque 底层实现原理

1. 分段连续数组结构(分块数组)

  • deque 并不是用一整块连续空间存放所有元素,而是分成若干个小块(如每块 64 个元素),每块用一段连续空间存储。
  • 一个指针数组(map)管理所有小块的位置。
  • map 本身也可以动态扩展(类似 vector 的扩容)。

2. 内存管理与扩容

  • 当头尾插入元素空间不足时,分配新的 block,并在 map 首尾插入/追加指向新 block 的指针。
  • 如果 map 空间不够,则重新分配更大的 map,将原有 block 指针迁移过去。
  • 这样避免了 vector 那种整体数据搬迁的高开销。

3. 随机访问实现

  • 下标访问时,先用下标和每块大小计算出元素所在的块和块内偏移,然后通过 map 定位到具体元素,仍为 O(1)。
  • 访问速度略逊于 vector,但远快于 list。

4. 插入与删除

  • 头部和尾部插入/删除:只需操作当前块或分配新块,O(1)。
  • 中间插入/删除:需要移动大量元素,O(n)。

5. 迭代器实现

  • 迭代器需要记录当前块的指针和块内元素的位置,不是裸指针。
  • 迭代器可支持 ++、–、+n、-n 等操作。

四、deque 的典型应用

  • 实现队列、双端队列(queue、deque)
  • 滑动窗口、单调队列
  • 需要两端频繁插入删除,且偶尔随机访问的场合

五、deque 的优缺点总结

特性vectordequelist
内存布局一块大数组分段小数组全部分散节点
随机访问O(1)O(1)O(n)
头尾插入删除尾 O(1)头尾 O(1)头尾 O(1)
中间插入删除O(n)O(n)O(1)
内存碎片化较少
迭代器失效扩容时全部失效头尾变动时部分插入/删除节点

六、代码示例

1. 基本插入、删除与访问

#include <deque>
#include <iostream>
using namespace std;

int main() {
    deque<int> dq;
    dq.push_back(1);      // 尾插
    dq.push_front(0);     // 头插
    dq.push_back(2);

    // 随机访问
    cout << dq[0] << " " << dq[1] << " " << dq[2] << endl; // 0 1 2

    // 头尾弹出
    dq.pop_front();
    dq.pop_back();
    cout << dq.front() << endl; // 1

    return 0;
}

2. 遍历与修改

#include <deque>
#include <iostream>
using namespace std;

int main() {
    deque<int> dq = {1, 2, 3, 4, 5};

    // 正向迭代器
    for(deque<int>::iterator it = dq.begin(); it != dq.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 反向迭代器
    for(deque<int>::reverse_iterator rit = dq.rbegin(); rit != dq.rend(); ++rit) {
        cout << *rit << " ";
    }
    cout << endl;

    // 使用范围for
    for(auto& v : dq) {
        v *= 2; // 修改元素
        cout << v << " ";
    }
    cout << endl;

    return 0;
}

3. 插入、删除与赋值操作

#include <deque>
#include <iostream>
using namespace std;

int main() {
    deque<int> dq = {1, 2, 3, 4, 5};

    // 在第3个元素前插入99
    dq.insert(dq.begin() + 2, 99);

    // 删除第2个元素
    dq.erase(dq.begin() + 1);

    // 批量赋值
    dq.assign(3, 42); // 清空后变为{42, 42, 42}

    // 清空
    dq.clear();

    // 重新赋值
    dq = {7, 8, 9};

    // 输出
    for(int x : dq) cout << x << " ";
    cout << endl;

    return 0;
}

4. 读写文件中的数据到deque

#include <deque>
#include <fstream>
#include <iostream>
using namespace std;

int main() {
    deque<string> dq;

    // 从文件读取
    ifstream fin("input.txt");
    string line;
    while (getline(fin, line)) {
        dq.push_back(line);
    }
    fin.close();

    // 追加写入文件
    ofstream fout("output.txt");
    for (const auto& s : dq) {
        fout << s << endl;
    }
    fout.close();

    return 0;
}

5. 使用STL算法

#include <deque>
#include <algorithm>
#include <iostream>
using namespace std;

int main() {
    deque<int> dq = {5, 3, 8, 1, 9};

    // 排序
    sort(dq.begin(), dq.end());

    // 查找
    auto it = find(dq.begin(), dq.end(), 8);
    if(it != dq.end()) cout << "Found 8 at position " << (it - dq.begin()) << endl;

    // 反转
    reverse(dq.begin(), dq.end());

    // 输出
    for(int x : dq) cout << x << " ";
    cout << endl;

    return 0;
}

七、总结

  • deque 是一种兼顾两端高效增删和随机访问的容器,底层采用分段数组+指针数组的结构设计,避免了 vector 的整体搬迁,也避免了 list 的高内存碎片和低效率随机访问。
  • 在需要两端频繁操作并兼顾随机访问的场合,deque 是非常优秀的选择。

八、参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

电子异术家

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

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

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

打赏作者

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

抵扣说明:

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

余额充值