学而时习之:C++中的标准模板5.1

12 阅读5分钟

C++ STL中的 Deque

顺序容器的底层数据结构及典型使用场景:

顺序容器底层数据结构使用场景
vector动态数组(连续内存空间)- 需要频繁随机访问元素
- 主要在尾部进行插入/删除操作
- 对内存连续性有要求(如与C API交互)
deque分段连续空间(中央控制器 + 多个缓冲区)- 需要在头尾两端进行高效的插入/删除
- 相比vector,中间插入更慢,但头尾操作更快
list双向链表(非连续内存)- 需要在任意位置频繁插入/删除元素
- 不需要随机访问(或随机访问极少)
- 对元素移动操作(如splice)有较高要求
forward_list单向链表- 场景与list类似,但只需单向遍历
- 对内存开销特别敏感(比list省一个指针)
- 不支持反向迭代器,不适合需要反向遍历的场景
array静态数组(连续内存,大小固定)- 需要在编译期确定元素个数
- 不涉及动态扩容
- 需要最小的内存开销和最高的访问效率

这些容器各有特点,选择使用时通常根据随机访问需求、插入/删除的位置以及内存布局要求来决定。

DequeDouble-Ended Queue(双端队列)的缩写。它是一种序列(顺序)容器,允许你高效地从前端(front)后端(back) 添加或删除元素。

image.png

语法

std::deque 类模板定义在 <deque> 头文件中。

deque<T> d;

其中:

符号含义
T双端队列中元素的数据类型
d为双端队列指定的名称

基本操作

双端队列的基本操作如下所示:

1.插入元素

在双端队列中,有两种插入操作:push_back()push_front()

push_back()

  • 在双端队列的 末尾 添加一个元素
  • 适用于追加数值
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> d;
    
    // Adding elements at the back
    d.push_back(10);
    d.push_back(20);
    d.push_back(30);
    
    // Displaying elements
    cout << "Elements in deque (added using push_back): ";
    for (int val : d) {
        cout << val << " ";
    }
    cout << endl;

    return 0;
}
Elements in deque (added using push_back): 10 20 30 

push_front()

  • 在双端队列的 前端(头部) 添加一个元素
  • 帮助在 开头位置 高效地插入数值
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> d;
    
    // Adding elements at the front
    d.push_front(30);
    d.push_front(20);
    d.push_front(10);
    
    // Displaying elements
    cout << "Elements in deque (added using push_front): ";
    for (int val : d) {
        cout << val << " ";
    }
    cout << endl;

    return 0;
}
Elements in deque (added using push_front): 10 20 30

2.删除操作

在双端队列中,有两种删除操作:pop_back()pop_front()

2.1 pop_back()

  • 移除双端队列中的最后一个元素
  • 当你想从尾部删除时使用

2.2 pop_front()

  • 移除双端队列中的第一个元素
  • 头部删除的高效方式

示例代码:

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

int main() {
    deque<int> dq = {10, 20, 30, 40};
    
    // 初始: [10, 20, 30, 40]
    
    dq.pop_front();  // 移除头部: [20, 30, 40]
    dq.pop_back();   // 移除尾部: [20, 30]
    
    for (int val : dq) {
        cout << val << " ";
    }
    // 输出: 20 30
    
    return 0;
}

注意: 调用 pop_back()pop_front() 之前,建议先用 empty() 检查队列是否为空,避免对空队列进行操作导致未定义行为。

3.访问元素

双端队列中的元素可以通过 front()back() 进行访问。

3.1 front()

  • 返回第一个元素
  • 不会移除该元素
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> d = {100, 200, 300};
    // 访问前端元素
    cout << "第一个元素(front)是:" << d.front() << endl;
    return 0;
}
第一个元素(front)是:100

3.2 back()

  • 返回最后一个元素
  • 适用于检查末尾的值
#include <iostream>
#include <deque>

using namespace std;

int main() {
    deque<int> d = {10, 20, 30, 40};
    // 访问最后一个元素
    cout << "最后一个元素(back)是:" << d.back() << endl;
    return 0;
}
最后一个元素(back)是:40

4.Size 和 Empty

4.1 size()

  • 返回元素的数量
  • 有助于循环遍历或检查长度
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> d = {5, 10, 15, 20};
    
    // 获取双端队列的大小
    cout << "双端队列中的元素数量是:"  << d.size() << endl;
    return 0;
}
双端队列中的元素数量是:4

4.2 empty()

  • 如果双端队列为空,则返回 true
  • 在访问元素前用于错误检查非常有用
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> d;
    if (d.empty()) {
        cout << "双端队列为空。" << endl;
    } else {
        cout << "双端队列不为空。" << endl;
    }
    
    // 添加一个元素后再次检查
    d.push_back(100);

    if (d.empty()) {
        cout << "双端队列为空。" << endl;
    } else {
        cout << "双端队列不为空。" << endl;
    }
    return 0;
}
双端队列为空。
双端队列不为空。

5.清空操作(Clear)

  • 移除双端队列中的所有元素
  • 执行后双端队列变为
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> d = {1, 2, 3, 4, 5};
    cout << "清空前,大小:" << d.size() << endl;
    
    // 清空双端队列中的所有元素
    d.clear();
    
    cout << "清空后,大小:" << d.size() << endl;

    if (d.empty()) {
        cout << "双端队列现在为空。" << endl;
    }
    return 0;
}
清空前,大小:5
清空后,大小:0
双端队列现在为空。

时间负责度

操作时间复杂度
在末尾插入(Insert at back)O(1) 均摊
在前端插入(Insert at front)O(1) 均摊
在任意位置插入(Insert at arbitrary position)O(n)
从末尾删除(Remove from back)O(1) 均摊
从前端删除(Remove from front)O(1) 均摊
从任意位置删除(Remove from arbitrary position)O(n)
使用索引访问任意位置的元素O(1)
使用索引更新任意位置的元素O(1)
遍历双端队列(Iterate the deque)O(n)

Queue vs Deque

特性队列(Queue)双端队列(Deque / Double-Ended Queue)
定义遵循 FIFO(先进先出)原则的线性数据结构队列的通用版本,允许从两端进行插入和删除
允许的操作入队(Enqueue,添加到尾部)
出队(Dequeue,从头部移除)
前端插入(Insert Front)
尾部插入(Insert Rear)
前端删除(Delete Front)
尾部删除(Delete Rear)
访问方式受限:只能在尾部插入,在头部删除更灵活:可以在前端和尾部进行插入和删除
使用场景需要严格 FIFO 顺序时
(例如:任务调度)
需要同时具备 FIFO 和 LIFO 行为时
(例如:滑动窗口问题、回文检查)
效率更简单,但功能有限稍微复杂一些,但更强大
类型简单队列
循环队列
优先队列
输入受限双端队列(仅尾部插入)
输出受限双端队列(仅头部删除)