C++30 STL容器 -deque双端队列

STL 容器 deque(双端队列)完全解析

你现在要了解的 deque(double-ended queue)是 C++ STL 中双端高效操作 的动态序列容器,它弥补了 vector 头部 / 中间操作低效的缺点,同时保留了随机访问的特性,是介于 vectorlist 之间的灵活选择。下面从「核心特性→基础用法→与 vector 对比→适用场景」全面讲解,新手也能快速理解。

一、核心特性(先搞懂本质)

  • 双端高效操作:头部 / 尾部插入 / 删除元素的时间复杂度都是 O(1)(核心优势);
  • 随机访问 :通过下标 [] 访问元素的时间复杂度 O(1),和 vector 一样;
  • 非连续内存 :底层是「分段连续内存」+ 中控数组(存储各段内存的指针),避免了 vector 扩容时的大规模拷贝;
  • 中间操作低效:插入 / 删除中间元素仍需移动后续元素,时间复杂度 O(n);
  • 无容量概念deque 没有 capacity() 方法,扩容时只需新增分段内存,无需拷贝原有元素,因此也没有 reserve() 方法。

二、基础用法(必掌握)

1. 头文件与命名空间

使用 deque 必须包含专属头文件,指定 std 命名空间更便捷:

cpp

运行

复制代码
#include <deque>  // 核心头文件
using namespace std; // 可选,否则需写 std::deque

2. 初始化(常见方式)

cpp

运行

复制代码
// 1. 空deque
deque<int> d1;

// 2. 初始化指定大小+默认值(8个元素,每个值为0)
deque<int> d2(8);

// 3. 初始化指定大小+自定义值(5个元素,每个值为9)
deque<int> d3(5, 9);

// 4. 用其他deque初始化(拷贝构造)
deque<int> d4(d3);

// 5. C++11 列表初始化(最简洁)
deque<int> d5 = {1,2,3,4,5};

// 6. 用迭代器范围初始化(适配其他容器)
vector<int> v = {6,7,8};
deque<int> d6(v.begin(), v.end()); // d6: [6,7,8]

3. 核心操作(增删改查)

(1)添加元素(双端 + 指定位置)

cpp

运行

复制代码
deque<int> d;

// 尾部添加(和vector一致)
d.push_back(10);  // d: [10]
// 头部添加(deque核心优势,vector无此高效方法)
d.push_front(5);  // d: [5,10]

// 指定位置插入(效率比vector略优,但仍为O(n))
d.insert(d.begin()+1, 7); // 在第2个位置插入7 → d: [5,7,10]
d.insert(d.end(), 2, 15); // 尾部插入2个15 → d: [5,7,10,15,15]
(2)删除元素(双端 + 指定位置)

cpp

运行

复制代码
// 尾部删除(和vector一致)
d.pop_back();  // 删除最后一个元素 → d: [5,7,10,15]
// 头部删除(deque核心优势)
d.pop_front(); // 删除第一个元素 → d: [7,10,15]

// 指定位置删除
d.erase(d.begin()+1); // 删除第2个元素 → d: [7,15]

// 清空所有元素(仅清空内容,释放分段内存)
d.clear(); 
(3)访问元素(和 vector 几乎一致)

cpp

运行

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

// 方式1:下标访问(无越界检查,速度快)
int a = d[2];  // a=3

// 方式2:at()访问(有越界检查,抛出out_of_range异常)
int b = d.at(2); // b=3

// 方式3:迭代器访问
for (deque<int>::iterator it = d.begin(); it != d.end(); ++it) {
    cout << *it << " "; // 输出:1 2 3 4
}

// 方式4:C++11 范围for
for (int num : d) {
    cout << num << " ";
}

// 方式5:访问首尾元素(快捷方式)
int first = d.front(); // first=1
int last = d.back();   // last=4
(4)修改元素

cpp

运行

复制代码
d[1] = 99;   // 下标修改 → d: [1,99,3,4]
d.at(2) = 88; // at()修改 → d: [1,99,88,4]
d.front() = 66; // 修改第一个元素 → d: [66,99,88,4]
d.back() = 77;  // 修改最后一个元素 → d: [66,99,88,77]

4. 常用属性 / 方法

cpp

运行

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

d.size();  // 元素个数 → 4
d.empty(); // 是否为空 → false
d.resize(6); // 调整大小为6,新增元素默认值0 → d: [1,2,3,4,0,0]
// 注意:deque 无 capacity()/reserve() 方法(无统一容量概念)

三、deque vs vector(核心区别,避坑关键)

特性 deque vector
内存布局 分段连续,中控数组管理 单一连续内存
首尾操作效率 头部 / 尾部均为 O (1)(高效) 尾部 O (1),头部 O (n)(低效)
随机访问效率 O (1)(略低于 vector,需计算分段) O (1)(最优)
扩容机制 新增分段,无大规模拷贝 重新分配整块内存 + 拷贝所有元素
容量相关方法 无 capacity ()/reserve () 有 capacity ()/reserve ()
迭代器稳定性 插入元素时,仅当前分段迭代器失效 扩容后所有迭代器 / 指针失效
内存利用率 分段可能有少量内存浪费 连续内存,利用率高

四、进阶技巧(避坑 + 优化)

1. 性能优化点

  • ✅ 优先用 push_front()/pop_front() 处理头部操作(这是 deque 的核心价值);
  • ✅ 避免频繁在中间插入删除(改用 list);
  • ✅ 若需大量随机访问 + 偶尔头部操作,选 deque;若仅尾部操作 + 大量随机访问,选 vector。

2. 常见坑点

  • ❌ 误以为 deque 是完全连续内存:分段连续导致「取地址后算术运算」可能出错(如 &d[0]+5 可能跨分段,结果错误);
  • ❌ 试图调用 capacity()/reserve():deque 无这两个方法,编译报错;
  • ❌ 追求极致随机访问效率选 deque:vector 的随机访问略快,仅当需要头部高效操作时才选 deque。

3. 典型使用场景

STL 中 queue(队列)和 stack(栈)的底层默认容器是 deque,而非 vector------ 因为 queue 需要头部出队、尾部入队,stack 需要尾部操作,deque 能更好适配这些场景的效率需求。

五、适用场景

✅ 推荐用 deque:

  • 需要头部 + 尾部频繁增删元素(如实现队列、双端队列逻辑);
  • 数据量不确定,且不想承受 vector 扩容的拷贝开销;
  • 需随机访问,同时偶尔有头部操作需求。

❌ 不推荐用 deque:

  • 追求极致的随机访问效率(选 vector);
  • 频繁在中间插入删除元素(选 list);
  • 需要将容器数据直接转换为原生连续数组(选 vector)。

总结

  1. deque 核心优势是双端高效操作(头部 / 尾部增删均为 O (1)),弥补了 vector 头部操作低效的缺点;
  2. 基础用法和 vector 高度相似,重点记住 push_front()/pop_front() 这两个独有的高效方法;
  3. 与 vector 的核心区别在内存布局:deque 分段连续,无容量概念,迭代器稳定性略优;
  4. 选型原则:头部操作多选 deque,仅尾部操作 + 极致随机访问选 vector。

简易实现deque容器

复制代码
#include<iostream>
#include<string.h>
#include<memory>
using namespace std;
template<class T>
class My_deque {
private:
    using value_type = T;
    using pointer = T*;
    using reference = T&;
    using iterator = T*;
    using const_iterator = const T*;
    pointer _start;
    pointer _finish;
    size_t _size;
    size_t _capacity;
public:
//===================构造与析构==================
    My_deque() {
       _start = nullptr;
         _finish = nullptr;
        _size = 0;
        _capacity = 0;
    }
    My_deque(size_t n) {
        _capacity = n;
        _size = n;
        _start = (pointer)operator new(sizeof(value_type) * _capacity);//new运算符
        _finish = _start + _size;
        for (size_t i = 0; i < n; ++i) {
            new (_start + i) T();//定位 new
        }
    }
    My_deque(size_t n, const T& value) {
        _capacity = n;
        _size = n;
        _start = (pointer)operator new(sizeof(value_type) * _capacity);//new运算符
        _finish = _start + _size;
        for (size_t i = 0; i < n; ++i) {
            new (_start + i) T(value);//定位 new
        }
    }
    My_deque(const My_deque& dq) {
        _capacity = dq._capacity;
        _size = dq._size;
        _start = (pointer)operator new(sizeof(value_type) * _capacity);
        _finish = _start + _size;
        for (size_t i = 0; i < _size; ++i) {
            new (_start + i) T(dq._start[i]);
        }
    }
    My_deque(initializer_list<T> ilist) {
        _capacity = ilist.size();
        _size = ilist.size();
        _start = (pointer)operator new(sizeof(value_type) * _capacity);
        _finish = _start + _size;
        size_t i = 0;
        for (const auto& elem : ilist) {
            new (_start + i) T(elem);
            ++i;
        }
    }
    ~My_deque() {
        for (size_t i = 0; i < _size; ++i) {
            _start[i].~T(); // 显式调用析构函数
        }
        operator delete(_start); // 释放内存
    }
    // Additional member functions to be implemented
    //===================迭代器接口==================
    iterator begin() noexcept {
        return _start;
    }
    iterator end() noexcept {
        return _finish;
    }   
    iterator cbegin() noexcept {
        return _start;
    }
    iterator cend() noexcept {
        return _finish;
    }
    //===================元素访问==================
    reference operator[](size_t i) {
        if (i < 0 || i >= _size) {
            exit(EXIT_FAILURE);
        }
        return _start[i];
    }
    reference at(size_t i) {
        if (i < 0 || i >= _size) {
            exit(EXIT_FAILURE);
        }
        return _start[i];   
    }
    reference front() {
        return _start[0];
    }
    reference back() {
        return _finish[-1];
    }
    pointer data() noexcept {
        return _start;
    }
    //===================容量==================
    size_t size() noexcept {
        return _size;
    }
    size_t capacity() noexcept {
        return _capacity;
    }
    bool empty() noexcept {
        return _size == 0;
    }
    //===================操作==================
    void push_back(const T& value) {
        if (_size >= _capacity) {
            size_t new_capacity = (_capacity == 0) ? 1 : _capacity * 2;
            pointer new_start = (pointer)operator new(sizeof(value_type) * new_capacity);
            for (size_t i = 0; i < _size; ++i) {
                new (new_start + i) T(std::move(_start[i]));
                _start[i].~T();
            }
            operator delete(_start);
            _start = new_start;
            _capacity = new_capacity;
        }
        new (_start + _size) T(value);
        ++_size;
        _finish = _start + _size;
    }
    void pop_back() {
        if (_size == 0) {
            exit(EXIT_FAILURE);
        }
        --_size;
        _finish = _start + _size;
        _start[_size].~T();
    }
    void push_front(const T& value) {
        if (_size >= _capacity) {
            size_t new_capacity = (_capacity == 0) ? 1 : _capacity * 2;
            pointer new_start = (pointer)operator new(sizeof(value_type) * new_capacity);
            for (size_t i = 0; i < _size; ++i) {
                new (new_start + i + 1) T(std::move(_start[i]));
                _start[i].~T();
            }
            operator delete(_start);
            _start = new_start;
            _capacity = new_capacity;
        } else {
            for (size_t i = _size; i > 0; --i) {
                new (_start + i) T(std::move(_start[i - 1]));
                _start[i - 1].~T();
            }
        }
        new (_start) T(value);
        ++_size;
        _finish = _start + _size;
    }
    void pop_front() {
        if (_size == 0) {
            exit(EXIT_FAILURE);
        }
        _start[0].~T();
        for (size_t i = 1; i < _size; ++i) {
            new (_start + i - 1) T(std::move(_start[i]));
            _start[i].~T();
        }
        --_size;
        _finish = _start + _size;
    }
    void insert(size_t index, const T& value) {
        if (index < 0 || index > _size) {
            exit(EXIT_FAILURE);
        }
        if (_size >= _capacity) {
            size_t new_capacity = (_capacity == 0) ? 1 : _capacity * 2;
            pointer new_start = (pointer)operator new(sizeof(value_type) * new_capacity);
            for (size_t i = 0; i < index; ++i) {
                new (new_start + i) T(std::move(_start[i]));
                _start[i].~T();
            }
            new (new_start + index) T(value);
            for (size_t i = index; i < _size; ++i) {
                new (new_start + i + 1) T(std::move(_start[i]));
                _start[i].~T();
            }
            operator delete(_start);
            _start = new_start;
            _capacity = new_capacity;
        } else {
            for (size_t i = _size; i > index; --i) {
                new (_start + i) T(std::move(_start[i - 1]));
                _start[i - 1].~T();
            }
            new (_start + index) T(value);
        }
        ++_size;
        _finish = _start + _size;
    }
    void erase(size_t index) {
        if (index < 0 || index >= _size) {
            exit(EXIT_FAILURE);
        }
        _start[index].~T();
        for (size_t i = index + 1; i < _size; ++i) {
            new (_start + i - 1) T(std::move(_start[i]));
            _start[i].~T();
        }
        --_size;
        _finish = _start + _size;
    }
    void clear() noexcept {
        for (size_t i = 0; i < _size; ++i) {
            _start[i].~T();
        }
        _size = 0;
        _finish = _start;
    }
    void swap(My_deque& other) noexcept {
        std::swap(_start, other._start);
        std::swap(_finish, other._finish);
        std::swap(_size, other._size);
        std::swap(_capacity, other._capacity);
    }
    void resize(size_t new_size, const T& value = T()) {
        if (new_size < _size) {
            for (size_t i = new_size; i < _size; ++i) {
                _start[i].~T();
            }
        } else if (new_size > _size) {
            if (new_size > _capacity) {
                size_t new_capacity = new_size;
                pointer new_start = (pointer)operator new(sizeof(value_type) * new_capacity);
                for (size_t i = 0; i < _size; ++i) {
                    new (new_start + i) T(std::move(_start[i]));
                    _start[i].~T();
                }
                operator delete(_start);
                _start = new_start;
                _capacity = new_capacity;
            }
            for (size_t i = _size; i < new_size; ++i) {
                new (_start + i) T(value);
            }
        }
        _size = new_size;
        _finish = _start + _size;
    }
};
int main(){
    //初始化测试
    My_deque<int> dq1;
    My_deque<int> dq2(5);
    My_deque<int> dq3(5, 10);
    My_deque<int> dq4 = {1, 2, 3, 4, 5};
    //元素访问测试
    dq3[2] = 20;
    cout << "dq3[2]: " << dq3.at(2) << endl;
    cout << "dq4 front: " << dq4.front() << ", back: " << dq4.back() << endl;
    //容量测试
    cout << "dq4 size: " << dq4.size() << ", empty: " << dq4.empty() << endl;
    //操作测试
    dq4.push_back(6);
    dq4.push_front(0);
    cout << "After push_back and push_front, dq4: ";
    for (auto it = dq4.begin(); it != dq4.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    dq4.pop_back();
    dq4.pop_front();
    cout << "After pop_back and pop_front, dq4: ";
    for (auto it = dq4.begin(); it != dq4.end(); ++it) {
        cout << *it << " ";
    }   
    cout << endl;
    dq4.insert(2, 15);
    cout << "After insert 15 at index 2, dq4: ";
    for (auto it = dq4.begin(); it != dq4.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    dq4.erase(2);
    cout << "After erase at index 2, dq4: ";
    for (auto it = dq4.begin(); it != dq4.end(); ++it)
    {
        cout << *it << " ";
    }
    cout << endl;
    dq4.resize(10, 100);
    cout << "After resize to 10 with fill value 100, dq4: ";
    for (auto it = dq4.begin(); it != dq4.end(); ++it)
    {
        cout << *it << " ";
    }
    cout << endl;
    dq4.clear();
    cout << "After clear, dq4 size: " << dq4.size() <<  " empty: " << dq4.empty() << endl;
    return 0;
}
相关推荐
你怎么知道我是队长2 小时前
C语言---文件读写
java·c语言·开发语言
AI视觉网奇2 小时前
ue 自己制作插件 c++
c++·ue5
xb11322 小时前
C#委托详解
开发语言·c#
brent4232 小时前
DAY50复习日
开发语言·python
木头程序员2 小时前
前端(包含HTML/JavaScript/DOM/BOM/jQuery)基础-暴力复习篇
开发语言·前端·javascript·ecmascript·es6·jquery·html5
Jayden_Ruan2 小时前
C++分解质因数
数据结构·c++·算法
Data_agent3 小时前
Cocbuy 模式淘宝 / 1688 代购系统(欧美市场)搭建指南
开发语言·python
lsx2024063 小时前
《Foundation 下拉菜单》
开发语言
期待のcode3 小时前
认识Java虚拟机
java·开发语言·jvm