C++基础笔记(二)队列deque,queue和堆priority_queue

一、数据结构说明
  1. queue
    一种先进先出的队列结构(FIFO),在stl里面的queue是一种适配器,也就是对于具体底层结构的封装。听不懂没关系,知道stl里面的queue一般不直接使用,而是用deque或者priority_queue代替就可以。默认的底层实现时deque
  2. deque
    deque的数据集结构是一种双向队列,允许在存储数据的两侧高效的插入/删除,也可以用来模拟stack(把一端的插入删除禁止了就行),两边插入删除的时间都是O(1),中间插入删除则是O(n),不过由于这种双端的特性,操作的开销是略大于vector的。
  3. priority_queue
    优先级队列或者叫堆都可以。简单来说只保证可以快速访问/移除最高优先级元素的数据结构,默认的是最大堆。
二、常见使用方法
C++ 复制代码
//构造上和vector差不多
// 常见构造
std::deque<int> d;                 // 空 deque
std::deque<int> d1(10);            // 包含 10 个默认值(0)
std::deque<int> d2(10, 5);         // 包含 10 个值为 5 的元素
std::deque<int> d3 = {1,2,3,4};    // 列表初始化
std::deque<int> d4(d3);            // 拷贝构造
std::deque<int> d5(std::move(d4)); // 移动构造

// 查看属性
d.size();      // 元素个数(size_type)
d.empty();     // 是否为空
// deque 没有 capacity() 接口(因为底层是分段数组,capacity 概念不同)
// 但可以通过 d.max_size() 查看理论上能容纳的最大元素数(实现相关)

// 改容量和大小(与 vector 不完全相同)
d.resize(n);          // 改变 size:变大时填充默认值(或指定值),变小时移除多余元素
d.clear();            // 清空元素,但底层块可能保留以便复用(capacity 不可见)
d.shrink_to_fit();    // 有些实现对 deque 支持有限,行为非必须(实现相关)

// 元素增删改查
d.push_back(x);       // 尾部添加 (amortized O(1))
d.pop_back();         // 删除尾部元素 (O(1))
d.push_front(x);      // 头部添加 (amortized O(1))
d.pop_front();        // 头部删除 (O(1))
d[i];                 // 随机访问 (O(1))
d.at(i);              // 带范围检查的访问,越界抛 out_of_range
d.front(); d.back();  // 访问首/尾元素(可读写)
d.insert(it, x);      // 在迭代器 it 位置插入(O(n))
d.erase(it);          // 擦除 it(返回下一个元素迭代器,O(n))
d.erase(begin, end);  // 区间擦除(O(n))

// emplace 系列
d.emplace_back(args...); // 直接在尾部构造(减少拷贝/移动)
d.emplace_front(args...);

// 遍历
for (size_t i = 0; i < d.size(); ++i)
    d[i] = static_cast<int>(i);

for (const auto &x : d)
    std::cout << x << ' ';

// 也可用迭代器,但是用得比较少,一般是范围for循环或者直接for
for (auto it = d.begin(); it != d.end(); ++it) { *it = *it + 1; }

// 排序(对元素重新排序),更换比较函数的话同vector
std::sort(d.begin(), d.end());            // deque 支持随机访问迭代器
std::stable_sort(d.begin(), d.end());     // 稳定排序(额外内存)
// 注意:排序会移动/拷贝元素,可能触发移动构造/拷贝构造

关于priority_queue也是差不多

复制代码
// priority_queue 构造方式与 vector 等普通容器不太相同,不直接支持 range, copy, move 等
// 常见构造
std::priority_queue<int> pq;                                 // 空的默认最大堆(top 最大)
std::priority_queue<int, std::vector<int>, std::greater<int>> pq_min; // 空的最小堆(top 最小),greater是内置的比较函数,就是升序变降序
std::priority_queue<int> pq_from_vec({1,2,3,4});            // 列表初始化,内部 O(N) 构建堆

// 从现有容器构造 (O(N) 性能)
std::vector<int> v = {3,1,4,1,5};
std::priority_queue<int> pq_init(v.begin(), v.end());       // 使用迭代器范围构造最大堆

// 查看属性
pq.size();      // 元素个数
pq.empty();     // 是否为空
// priority_queue 没有 capacity() 接口,因为其本质是适配器

// priority_queue 没有 resize() / clear() 这种直接改大小和容量的接口
// 若需清空,只能不断 pop
while(!pq.empty()) pq.pop(); // 清空所有元素

// 元素增删查
pq.push(x);        // 插入元素,O(log N)
pq.emplace(args...); // 原位构造并插入元素,O(log N)
pq.pop();          // 弹出 top 元素,O(log N)
pq.top();          // 访问 top 元素(只读),O(1)
// priority_queue 不支持随机访问 (pq[i] 或 pq.at(i)),也不支持 insert/erase 中间元素

// 遍历,一般不使用,都是直接不断pop
// priority_queue 不提供迭代器,不能直接使用范围 for 或迭代器循环
// 遍历需拷贝一份,然后不断 pop
// std::priority_queue<int> temp_pq = pq;
// while(!temp_pq.empty()) { std::cout << temp_pq.top() << " "; temp_pq.pop(); }

// 排序,不支持
// priority_queue 不支持 std::sort,因为其不提供随机访问迭代器
// 若需要有序输出,只能通过不断 pop 来获取排好序的元素(从大到小或从小到大)
三、底层实现
  1. deque。主要通过多个固定大小的块(block)实现,每个块可以容纳B个元素(看具体实现),这些块的指针存放在一个map(这里的map只是个简单的数组,不是stl的map)。结构上是块内连续,块间不连续。也正因为这种结构,块中间的插入删除需要的时间复杂度来到O(n),随机访存虽然也是O(1)但是比vector低效一点。
  2. priority_queue。默认的底层是vector,通过堆生成算法来实现逻辑结构,二叉堆的插入和删除参考二叉堆算法,时间复杂度都是O(long(n)),不过全部都出栈算上调整的时间就来到了O(log(n)),在实际的使用基本只用来找有限个的最值
四、注意事项

由于deque不完全连续的实现思想,在插入/删除时,如果涉及到新块扩容(增加一个新的block)有可能导致迭代器失效,建议稳妥的做法时涉及到频繁的删除就用list链表,别用的队列实现,或者分两边操作,第一遍只遍历检查,确定要删除的数据,第二遍开始从后往前删除(一般情况不建议删除,加个标记表示这个数据失效就行,比如用一个set或者数组记录哪些元素失效)。

相关推荐
Hui Baby2 小时前
LSM 原理、实现及与 B+ 树的核心区别
java·linux·算法
Tadas-Gao2 小时前
存储技术革命:SSD、PCIe与NVMe的创新架构设计与性能优化
java·性能优化·架构·系统架构·存储
codergjw2 小时前
常见面试题
java
咕噜企业分发小米2 小时前
如何平衡服务器内存使用率和系统稳定性?
java·服务器·前端
李子园的李2 小时前
函数式编程与传统编程的对比——基于java
java
爬山算法2 小时前
Netty(13)Netty中的事件和回调机制
java·前端·算法
YJlio2 小时前
ZoomIt 学习笔记(11.7):安装与基础使用——演示/授课/录屏的神级放大镜
笔记·学习·intellij-idea
玉树临风ives2 小时前
atcoder ABC436 题解
c++·算法·leetcode·atcoder·信息学奥赛
fpcc2 小时前
C++23中的自定义模块开发
c++·c++23