stack-queue与deque-适配器模式中控数组分段连续与仿函数

stack、queue 与 deque:适配器模式、中控数组的分段连续,与仿函数

我的github:(https://github.com/xcx55/ubuntu-linux-project)

感谢各位大佬参观我的github!!

一、stack/queue:连迭代器都不给

栈和队列的接口一眼看过去就知道它们很"专制":不能用迭代器遍历 。原因不玄学------栈和队列是有明确顺序的 ,栈只能从顶上进出、队列只能头进尾出,要是开放迭代器随便跳着访问,这两个结构的语义就被破坏了。所以它们连 begin()/end() 都没有,只有 push/pop/top(front)/empty/size 这几个"守规矩"的接口。

手搓的时候也轻:栈的底层直接复用顺序表或链表,push 就是尾插、pop 就是尾删;队列保证队尾是 nullptr (若底层用链表,则做成循环链表,head 尾相接)------初始化列表是成员定义的地方,这些原则在手搓容器时全部复用。

二、适配器模式:不写数据结构,只写"包装"

这里出现了 STL 六大组件(容器、算法、迭代器、适配器 、仿函数、空间配置器)里还没见过的一位:适配器也是类,但它不管理数据,只把已有容器的接口"翻译"成自己的语义:

cpp 复制代码
template <class T, class container = std::deque<int>>
class stack {
public:
    void push(T x) { _con.push_back(x); }
    void pop()     { _con.pop_back(); }
    ...
};

stack 和 queue 的底层是有一个数据结构的 ,而且这个底层容器是模板参数------你可以自由选择想用的底层数据结构 :想要 vector 的就传 vector,想要 list 的就传 list。container 的缺省值一般是双端队列 deque。

这就是适配器模式的精髓:stack/queue 不实现任何数据结构,只定义"怎么用" ;vector 管存、list 管存、deque 管存,stack 只规定"后进先出"。一句话总结:将 vector 和 list 的两个 container 用 deque 代替------了解双端队列,成了这一章的正事。

三、deque:vector 和 list 的结合

先看两个极致(笔记里的原表):

优点 缺点
数组(vector) 下标随机访问快、尾插尾删效率高、CPU 高速缓存命中率高 头部/中间插入删除低下;扩容有性能消耗、倍数扩容可能空间浪费
链表(list) 任意位置 O(1) 插入删除、按需申请释放 不支持下标随机访问;缓存命中率低,还存在缓存污染

从功能看,deque 是 vector 和 list 的结合:它有 list 友好的头尾插入删除(push_front/push_back/pop_front/pop_back 一应俱全),又有 vector 的快速下标访问(operator\[\]/at/front/back)。对照接口表能看得很清楚------deque 的成员函数一半画线连向 vector、一半连向 list。

但中间插入的大量数据挪移它解决不了------这是 deque 天生的折衷。

四、deque 底层:中控数组 + 分段连续

deque 的底层结构是本章最精彩的部分:

  • 一段段**定长缓冲区(buff 数组)**存真正的数据;
  • 一个中控数组 map 统一管理这些缓冲区------map 存的是数组首元素地址的指针,所以 map 本身是二级指针数组,node 是指向一级指针的二级指针;
  • map 满载了,就再找一块更大的空间做 map(reallocate_map),把旧的一级指针搬过去------和 vector 扩容一个思路,只不过搬的是指针不是数据。

双端队列底层是一段假象的连续空间,实际是分段连续的。为了维护"整体连续"以及随机访问的假象,重担全落在 deque 的迭代器身上------每个迭代器四个指针:

复制代码
cur   --------- 当前指向的位置(核心!迭代器现在指向的就是 cur)
first --------- 当前 buff 数组的首空间
last  --------- 当前 buff 数组的末空间(无数据)
node  --------- 指向中控数组里的那个一级指针

跨缓冲区时 ++ 就是"cur 撞到 last → node 跳到 map 里下一个一级指针 → cur/first/last 重新锚定到新 buff"。所以 deque 的迭代器是 STL 里最复杂的迭代器之一------复杂度没有消失,只是从用户转移到了迭代器类里。

定位一句话:deque 很平均 ------头尾 O(1)、随机访问 O(1)(但常数大),中间插入不行。哪里都不极致,所以它适合做默认参数:给 stack/queue 当底层(只用头尾操作,它的"平均"毫无短板),而真正排序遍历的场合大家还是用 vector/list。

五、仿函数:为了不再用 C 的函数指针

优先级队列(priority_queue,本质就是堆)带来了新需求:默认大根堆,但有时想要小根堆------比较逻辑要能被用户换掉。C 语言的做法是 qsort 那种函数指针:

c 复制代码
void qsort(void* base, size_t num, size_t size, int (*compar)(const void*, const void*));

C++ 的答案是仿函数/函数对象 :本质是类,重载了 operator(),所以能像函数一样加括号调用,还能保存状态(普通函数做不到):

cpp 复制代码
class Less {
public:
    bool operator()(int x, int y) { return x < y; }
};

Less lessFunc;
cout << lessFunc(1, 2) << endl;   // 对象当函数用

仿函数传入类型或对象,有了实例,实例() 就是你要的需求 。在 priority_queue 里,Compare 是一个类参数、Container 是底层容器参数------仿函数本质是类,不是函数 ,所以它作为模板参数传的是类型,用的时候再实例化。

于是有了那个经典的反直觉写法:

cpp 复制代码
priority_queue<int> pq1;                          // 默认 less → 大根堆
priority_queue<int, vector<int>, greater<int>> pq2;  // greater → 小根堆

为什么一个带括号一个不带? 一个是传类型 (greater 是实例化类型,不带括号),一个是传匿名对象 (某些场景传对象,带括号)------类型参数和对象参数是两个世界。默认传 less 得到大根堆:less 意为"父 < 子"成立时上浮,大的自然浮到顶上。

六、建堆收口:向上/向下调整的前提

优先队列的 push/pop 就是堆的向上/向下调整,笔记里两条结论值得原样保留:

  • 使用向上或向下调整的前提:除一个点以外,其余部分已经是合法堆;
  • 向上调整建堆是"边插边建" (while 条件是 child > 0,只和父节点比,不用和兄弟节点比);向下调整建堆是"查完了再建" (从最后一个非叶子节点往前,while 条件和容量挂钩,向下调整要先和兄弟节点比较选出子节点再和父节点比)。

两种建堆方式相互独立又相互映射,选哪种取决于你的数据是"一次全给"还是"一个个来"------优先队列的 push 走向上、pop(top 换尾再向下)走向下,正好各用各的。

收个尾

这一章的链路是:stack/queue 有明确顺序 → 不给迭代器 → 只做容器语义的包装 → 适配器模式 → 底层缺省 deque → deque 用中控数组伪造连续 → 复杂度全压给迭代器 ;另一条线是比较逻辑要可换 → 函数指针不够优雅 → 仿函数(重载 operator() 的类)→ priority_queue 默认大根堆 less、greater 换小根堆。

六大组件至此集齐:容器管存、算法管算、迭代器当胶水、适配器管翻译、仿函数管策略。

相关推荐
Eyfcom1 天前
〖物流系统架构演进实录 02〗物流网关设计:多平台接入适配与异构接口的统一收敛
适配器模式·物流管理系统·物流架构·网关设计·多平台对接
yuehuaxin0111 天前
LS8011C 快充协议芯片|SOT‑23‑6L 内置 431,PD3.2 SPR/PPS 适配器物料备选评估调试笔记
笔记·智能家居·适配器模式·智能硬件
yuehuaxin0111 天前
LP3717BSL 芯茂微|SOP8L 自供电 PSR,SEL 电阻调峰值电流,12W 充电器物料替代评估笔记
嵌入式硬件·适配器模式·智能硬件
缓慢更新12 天前
表单引擎的存储建模:宽表与文档库的取舍
运维·服务器·网络·数据库·低代码·oracle·适配器模式
博风14 天前
8、设计模式应用举例:适配器模式:Iterator接口与Enumeration接口
设计模式·适配器模式
范中勤17 天前
适配器模式(Adapter Pattern):原理、场景与 LLM 多厂商工程落地
设计模式·架构·langchain·适配器模式·多厂商
yuehuaxin0118 天前
上海贝岭 BL6523GX(D78) 单相计量芯片开发笔记:引脚定义、寄存器配置与校表流程
嵌入式硬件·智能家居·适配器模式·智能硬件
yuehuaxin0123 天前
LP20R100FN 同步整流|3C 认证适配器 CCM 工况倒灌、温升超标样机避坑解析
嵌入式硬件·智能家居·适配器模式·智能硬件
yuehuaxin0123 天前
LP3710S 原边 PSR|3C 认证适配器 Peak‑Load 瞬时冲击、EMI 传导骚扰样机避坑解析
嵌入式硬件·适配器模式·智能硬件