STL 容器适配器:stack、queue 与 priority_queue

在 C++ STL 的体系中,容器适配器(Container Adapter)是一类特殊的 "容器"------ 它们并非原生容器,而是通过适配底层容器的接口,封装出特定的访问逻辑。本文将从容器适配器的核心设计思想出发,详解 stack、queue、priority_queue 的实现原理,同时拆解仿函数、模板特化等关键配套技术,带你吃透 STL 适配器的设计精髓。

一、容器适配器的核心思想

适配器是一种经典的设计模式,核心是 "接口转换":将一个类的接口转换成客户期望的另一个接口。STL 中的 stack、queue、priority_queue 均属于适配器,它们不直接实现数据存储,而是复用 deque、vector、list 等原生容器的底层能力,仅封装上层的访问规则。

比如 stack 的 "后进先出"、queue 的 "先进先出"、priority_queue 的 "按优先级访问",本质都是对底层容器接口的筛选和重组 ------ 这也是适配器模式的核心价值:复用现有逻辑,定制特定行为

二、stack 与 queue:基于 deque 的适配实现

stack(栈)和 queue(队列)是最基础的适配器,默认选择 deque 作为底层容器,这与 deque 的特性密切相关。

1. deque:vector 与 list 的 "缝合怪"

deque(双端队列)平衡了 vector 和 list 的优缺点,成为 stack/queue 的默认适配容器:

|-----------|--------------|----------|-------------------|
| 特性 | vector | list | deque |
| 头插 / 尾插效率 | 尾插优、头插 O (N) | 均为 O (1) | 均为 O (1)(效率更高) |
| 随机访问效率 | O (1)(最优) | O(N) | O (1)(略慢于 vector) |
| 空间扩容 | 拷贝 + 浪费内存 | 按需申请 | 分段扩容(无拷贝) |

deque 的 "分段连续" 结构,既支持高效的头尾操作,又避免了 vector 扩容的代价、list 随机访问的低效,因此成为 stack/queue 的最优选择。

2. stack:后进先出的封装

stack 将底层容器的 "尾部" 作为栈顶,仅暴露 push/pop/top 等核心接口,屏蔽其他无关操作:

cpp 复制代码
#pragma once
#include <deque>

namespace bit
{
    template<class T, class Container = deque<T>>
    class stack
    {
    public:
        void push(const T& x)  { _con.push_back(x); } // 尾插=入栈
        void pop() { _con.pop_back(); } // 尾删=出栈
        const T& top() const { return _con.back(); } // 取尾部=栈顶
        size_t size() const { return _con.size(); }
        bool empty() const { return _con.empty(); }

    private:
        Container _con; // 复用底层容器的存储逻辑
    };
}

stack 的适配逻辑极其简洁:所有操作都映射到底层容器的 "尾部操作",且支持替换底层容器(如 vector、list),只要容器提供push_back/pop_back/back接口即可。

3. queue:先进先出的封装

queue 需要 "尾部插入、头部删除",因此适配的容器需支持push_back/pop_front------ 这也是 queue 默认不用 vector 的原因(vector 无pop_front接口):

cpp 复制代码
#pragma once
#include <deque>

namespace bit
{
    template<class T, class Container = deque<T>>
    class queue
    {
    public:
        void push(const T& x) { _con.push_back(x); } // 尾插=入队
        void pop() { _con.pop_front(); } // 头删=出队
        const T& front() const { return _con.front(); } // 队头
        const T& back() const { return _con.back(); } // 队尾
        size_t size() const { return _con.size(); }
        bool empty() const { return _con.empty(); }

    private:
        Container _con;
    };
}

三、priority_queue:基于堆与仿函数的优先级队列

priority_queue(优先级队列)是更复杂的适配器:底层基于堆结构(默认 vector 存储),通过仿函数控制优先级规则。

1. 堆的核心操作:向上 / 向下调整

priority_queue 的核心是维护一个堆(默认大顶堆),插入 / 删除元素时需通过AdjustUp/AdjustDown调整堆结构:

cpp 复制代码
#pragma once
#include <vector>

template<class T>
class Less { // 默认大顶堆的比较规则
public:
    bool operator()(const T& x, const T& y) { return x < y; }
};

namespace ssp
{
    template<class T, class Container = vector<T>, class Compare = Less<T>>
    class priority_queue
    {
    public:
        // 向上调整(插入元素后)
        void AdjustUp(int child)
        {
            Compare com;
            size_t parent = (child - 1) / 2;
            while (child > 0)
            {
                if (com(_con[parent], _con[child])) // 父 < 子,交换(大顶堆)
                {
                    swap(_con[child], _con[parent]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else break;
            }
        }

        // 向下调整(删除堆顶后)
        void AdjustDown(int parent)
        {
            Compare com;
            size_t child = 2 * parent + 1;
            while (child < _con.size())
            {
                // 找左右孩子中优先级更高的那个
                if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
                    child += 1;
                // 父 < 子,交换
                if (com(_con[parent], _con[child]))
                {
                    swap(_con[child], _con[parent]);
                    parent = child;
                    child = 2 * parent + 1;
                }
                else break;
            }
        }

        void push(const T& x)
        {
            _con.push_back(x);
            AdjustUp(_con.size() - 1); // 尾插后调整
        }

        void pop()
        {
            swap(_con[0], _con[_con.size() - 1]); // 堆顶与堆尾交换
            _con.pop_back(); // 删堆尾(原堆顶)
            AdjustDown(0); // 调整新堆顶
        }

        const T& top() { return _con[0]; } // 堆顶=优先级最高元素

    private:
        Container _con;
    };
}

2. 仿函数:定制优先级规则

仿函数是**"重载了 operator () 的类"**,其对象可像函数一样调用,核心作用是封装比较逻辑 ------ 这让 priority_queue 支持自定义优先级。

(1)仿函数的基础定义

cpp 复制代码
template<class T>
class Greater { // 小顶堆的比较规则
public:
    bool operator()(const T& x, const T& y) { return x > y; }
};

// 使用小顶堆
priority_queue<int, vector<int>, Greater<int>> pq;

(2)自定义类型的优先级控制

当 priority_queue 存储自定义类型(如Date*)时,默认按指针地址比较,需自定义仿函数:

cpp 复制代码
class Date {
public:
    Date(int year, int month, int day) : _year(year), _month(month), _day(day) {}
    bool operator<(const Date& d) const { // 自定义比较逻辑
        return (_year < d._year) || 
               (_year == d._year && _month < d._month) ||
               (_year == d._year && _month == d._month && _day < d._day);
    }
private:
    int _year, _month, _day;
};

// 自定义仿函数:按Date内容比较指针
class DateLess {
public:
    bool operator()(Date* p1, Date* p2) { return *p1 < *p2; }
};

// 使用:按Date内容维护大顶堆
priority_queue<Date*, vector<Date*>, DateLess> q;

3. 仿函数的通用场景:定制算法逻辑

仿函数不仅用于 priority_queue,还可用于定制算法规则(如排序):

cpp 复制代码
template<class Compare>
void BubbleSort(int* a, int n, Compare com)
{
    for (int i = 0; i < n; i++)
    {
        int flag = 0;
        for (int j = 1; j < n - i; j++)
        {
            if (com(a[j], a[j - 1])) // 仿函数控制比较规则
            {
                swap(a[j - 1], a[j]);
                flag = 1;
            }
        }
        if (flag == 0) break;
    }
}

// 升序排序(Less)、降序排序(Greater)
int a[] = {9,1,2,6,5,7,3,4};
BubbleSort(a, sizeof(a)/sizeof(int), Less<int>()); // 升序
BubbleSort(a, sizeof(a)/sizeof(int), Greater<int>()); // 降序
相关推荐
ytttr8732 小时前
隐马尔可夫模型(HMM)MATLAB实现范例
开发语言·算法·matlab
天远Date Lab2 小时前
Python实战:对接天远数据手机号码归属地API,实现精准用户分群与本地化运营
大数据·开发语言·python
listhi5202 小时前
基于Gabor纹理特征与K-means聚类的图像分割(Matlab实现)
开发语言·matlab
qq_433776423 小时前
【无标题】
开发语言·php
会周易的程序员3 小时前
多模态AI 基于工业级编译技术的PLC数据结构解析与映射工具
数据结构·c++·人工智能·单例模式·信息可视化·架构
Davina_yu3 小时前
Windows 下升级 R 语言至最新版
开发语言·windows·r语言
阿珊和她的猫3 小时前
IIFE:JavaScript 中的立即调用函数表达式
开发语言·javascript·状态模式
listhi5204 小时前
卷积码编码和维特比译码的MATLAB仿真程序
开发语言·matlab
yuan199974 小时前
基于主成分分析(PCA)的故障诊断MATLAB仿真
开发语言·matlab
J_liaty4 小时前
Java版本演进:从JDK 8到JDK 21的特性革命与对比分析
java·开发语言·jdk