STL deque 的详细特征

STL deque 的详细特征

  1. 基本特性
cpp 复制代码
#include <deque>
using namespace std;

deque<int> dq;  // 声明一个int类型的双端队列

· 双端队列:允许在两端进行高效插入和删除

· 动态数组:支持随机访问,可以像数组一样通过下标访问

· 内存结构:分段连续存储,由多个固定大小的内存块组成

  1. 内存结构与实现原理

内部结构示意图

复制代码
deque内存布局:
[块1] → [块2] → [块3] → [块4] → [块5]
  ↓       ↓       ↓       ↓       ↓
[ ] [ ]  [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
[ ] [ ]  [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
↑front   ↑back

每个块:固定大小(通常是512字节或容纳元素的内存对齐大小)
维护一个中央控制器(map):存储各个块的指针

关键特征

cpp 复制代码
// 1. 分块存储
// deque将元素存储在多个固定大小的内存块中
// 当需要扩容时,分配新的内存块,而不是重新分配整个数组

// 2. 随机访问复杂度 O(1)
deque<int> dq = {1, 2, 3, 4, 5};
cout << dq[2];  // O(1) 访问

// 3. 两端操作复杂度 O(1)
dq.push_front(0);  // O(1)
dq.push_back(6);   // O(1)
dq.pop_front();    // O(1)
dq.pop_back();     // O(1)
  1. 时间复杂度分析

操作 时间复杂度 说明

push_front() O(1) 平均分摊

push_back() O(1) 平均分摊

pop_front() O(1) 平均分摊

pop_back() O(1) 平均分摊

operator[] O(1) 随机访问

insert() O(n) 中间插入

erase() O(n) 中间删除

front()/back() O(1) 访问首尾

  1. 与 vector/list 的对比

性能对比

cpp 复制代码
#include <deque>
#include <vector>
#include <list>
#include <iostream>

void benchmark() {
    // 1. 前端插入 - deque 最优
    deque<int> dq;
    vector<int> vec;
    list<int> lst;
    
    // push_front 测试
    for(int i = 0; i < 100000; ++i) {
        dq.push_front(i);  // 快:O(1)
        lst.push_front(i); // 快:O(1)
        vec.insert(vec.begin(), i); // 慢:O(n)
    }
}

详细对比表

特性 deque vector list

内存布局 分段连续 连续 不连续

随机访问 O(1) O(1) O(n)

前端插入 O(1) O(n) O(1)

后端插入 O(1) 分摊O(1) O(1)

中间插入 O(n) O(n) O(1)

内存开销 中等 低 高

缓存友好 中等 高 低

迭代器类型 随机访问 随机访问 双向

  1. 迭代器特征

迭代器特性

cpp 复制代码
deque<int> dq = {1, 2, 3, 4, 5};

// 1. 随机访问迭代器
auto it = dq.begin();
it += 3;  // 支持随机访问
cout << *it;  // 输出4

// 2. 迭代器失效规则
dq.push_front(0);  // 所有迭代器失效!
dq.push_back(6);   // 所有迭代器失效!

// 3. 插入/删除中间元素
it = dq.begin() + 2;
dq.insert(it, 99);  // it失效

迭代器失效情况

操作 迭代器失效情况

push_front() 所有迭代器失效

push_back() 所有迭代器失效

pop_front() 指向被删元素的迭代器失效

pop_back() 指向被删元素的迭代器失效

insert() 所有迭代器失效

erase() 被删位置之后的迭代器失效

resize() 所有迭代器失效

  1. 常用操作示例

基本操作

cpp 复制代码
#include <deque>
#include <iostream>
#include <algorithm>

void basic_operations() {
    deque<int> dq;
    
    // 1. 两端插入
    dq.push_back(10);     // 后: 10
    dq.push_front(5);     // 前: 5, 后: 10 → 5,10
    dq.emplace_back(15);  // C++11: 5,10,15
    dq.emplace_front(0);  // C++11: 0,5,10,15
    
    // 2. 访问元素
    cout << "第一个元素: " << dq.front() << endl;  // 0
    cout << "最后一个元素: " << dq.back() << endl;  // 15
    cout << "第三个元素: " << dq[2] << endl;       // 10
    cout << "大小: " << dq.size() << endl;         // 4
    
    // 3. 删除元素
    dq.pop_front();  // 移除0 → 5,10,15
    dq.pop_back();   // 移除15 → 5,10
    
    // 4. 中间操作
    dq.insert(dq.begin() + 1, 7);  // 5,7,10
    dq.erase(dq.begin() + 1);      // 移除7 → 5,10
    
    // 5. 清空和检查
    dq.clear();
    cout << "是否为空: " << dq.empty() << endl;  // true
}

高级用法

cpp 复制代码
void advanced_usage() {
    // 1. 构造函数
    deque<int> dq1(5, 100);          // 5个100
    deque<int> dq2 = {1, 2, 3, 4, 5}; // 初始化列表
    deque<int> dq3(dq2.begin(), dq2.begin() + 3); // 复制部分
    
    // 2. 交换
    deque<int> a = {1, 2, 3};
    deque<int> b = {4, 5, 6};
    a.swap(b);  // O(1)操作
    
    // 3. 调整大小
    deque<int> dq = {1, 2, 3};
    dq.resize(5);      // 变为: 1,2,3,0,0
    dq.resize(2);      // 变为: 1,2
    dq.resize(4, 99);  // 变为: 1,2,99,99
    
    // 4. 容量相关
    deque<int> dq4;
    dq4.reserve(100);  // ❌ deque没有reserve()!
    // 只能通过resize预先分配空间
    dq4.resize(100);   // 分配100个元素空间
}
  1. 实际应用场景

场景1:滑动窗口

cpp 复制代码
// 滑动窗口最大值
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    deque<int> dq;  // 存储下标
    vector<int> result;
    
    for (int i = 0; i < nums.size(); ++i) {
        // 移除窗口外的元素
        if (!dq.empty() && dq.front() == i - k)
            dq.pop_front();
        
        // 维护单调递减队列
        while (!dq.empty() && nums[dq.back()] < nums[i])
            dq.pop_back();
        
        dq.push_back(i);
        
        if (i >= k - 1)
            result.push_back(nums[dq.front()]);
    }
    return result;
}

场景2:任务队列

cpp 复制代码
class TaskScheduler {
private:
    deque<pair<int, string>> taskQueue;  // 优先级, 任务名
    
public:
    void addHighPriorityTask(string task) {
        taskQueue.push_front({1, task});  // 前端插入
    }
    
    void addLowPriorityTask(string task) {
        taskQueue.push_back({0, task});   // 后端插入
    }
    
    string getNextTask() {
        if (taskQueue.empty()) return "";
        string task = taskQueue.front().second;
        taskQueue.pop_front();
        return task;
    }
};

场景3:回文字符串判断

cpp 复制代码
bool isPalindrome(const string& s) {
    deque<char> dq;
    
    // 填充deque(忽略空格和大小写)
    for (char c : s) {
        if (isalnum(c))
            dq.push_back(tolower(c));
    }
    
    // 两端比较
    while (dq.size() > 1) {
        if (dq.front() != dq.back())
            return false;
        dq.pop_front();
        dq.pop_back();
    }
    return true;
}
  1. 性能优化技巧

优化建议

cpp 复制代码
// 1. 使用emplace代替push(C++11及以上)
deque<pair<int, string>> dq;
dq.emplace_back(1, "test");  // 避免临时对象构造
// 等价于: dq.push_back(make_pair(1, "test"));

// 2. 预先分配空间
deque<int> dq;
dq.resize(1000);  // 如果知道大致大小,预先分配

// 3. 批量操作使用assign
dq.assign(100, 0);  // 100个0,比循环push_back快

// 4. 避免频繁的中间插入删除
// 如果需要频繁中间操作,考虑使用list

// 5. 使用swap释放内存
deque<int> dq;
// ... 使用dq ...
deque<int>().swap(dq);  // 清空并释放内存
  1. 注意事项

常见陷阱

cpp 复制代码
// 1. 迭代器失效问题
deque<int> dq = {1, 2, 3, 4, 5};
auto it = dq.begin() + 2;
dq.push_front(0);  // ❌ it失效!
// cout << *it;  // 未定义行为

// 2. 没有capacity()和reserve()
deque<int> dq;
// dq.capacity();  // ❌ 没有这个函数
// dq.reserve(100); // ❌ 没有这个函数

// 3. 中间操作性能差
for (int i = 0; i < 10000; ++i) {
    dq.insert(dq.begin() + dq.size()/2, i);  // O(n) 很慢!
}

// 4. 内存不是完全连续
int* ptr = &dq[0];
// ptr + dq.size() 可能不指向有效内存!

最佳实践

  1. 优先使用deque的场景:

    · 需要双端队列功能

    · 既需要随机访问又需要两端操作

    · 不确定前端还是后端操作更频繁

  2. 选择vector的场景:

    · 主要在后端操作

    · 需要连续内存

    · 需要最高缓存友好性

  3. 选择list的场景:

    · 频繁在中间插入删除

    · 不需要随机访问

    · 需要稳定的迭代器

  4. C++11/14/17/20 新特性

cpp 复制代码
// C++11: emplace, initializer_list
deque<int> dq = {1, 2, 3, 4, 5};
dq.emplace_front(0);

// C++17: try_emplace, insert_or_assign (对于map)
// deque本身变化不大

// C++20: 范围构造
vector<int> vec = {1, 2, 3, 4, 5};
deque<int> dq(vec.begin(), vec.end());

// C++20: 概念约束
template<typename T>
requires std::random_access_iterator<typename T::iterator>
void process(T& container) {
    // 只接受支持随机访问的容器
}

总结:deque是STL中一个平衡了随机访问和两端操作性能的数据结构,适用于特定的使用场景,但需要注意其迭代器失效规则和内存特性。

相关推荐
yongui478346 小时前
MATLAB 二维方腔自然对流 SIMPLE 算法
人工智能·算法·matlab
二进制coder6 小时前
C++ 中的 Interface:概念、实现与应用详解
开发语言·c++
循着风6 小时前
环形子数组的最大和
数据结构·算法·leetcode
CoovallyAIHub6 小时前
如何让AI的数据标注“火眼金睛”?人机协同才是可靠途径
深度学习·算法·计算机视觉
wa的一声哭了6 小时前
拉格朗日插值
人工智能·线性代数·算法·机器学习·计算机视觉·自然语言处理·矩阵
小年糕是糕手6 小时前
【C++同步练习】模板初阶
服务器·开发语言·前端·javascript·数据库·c++·改行学it
gongfuyd6 小时前
傅里叶变换、拉普拉斯变换、Z 变换的定义及关系
算法·机器学习·概率论
珂朵莉MM6 小时前
第七届全球校园人工智能算法精英大赛-算法巅峰赛产业命题赛第三赛季--前五题总结
人工智能·算法
啊阿狸不会拉杆6 小时前
《数字图像处理》第2章-数字图像基础
图像处理·python·算法·计算机视觉·数字图像处理