容器适配器深度解析:从STL的stack、queue到优先队列的底层实现

文章目录

  • 容器适配器深度解析:从STL的stack、queue到优先队列的底层实现
    • [1. 栈的介绍和使用](#1. 栈的介绍和使用)
    • [2. 队列的介绍和使用](#2. 队列的介绍和使用)
      • [2.1 队列的介绍](#2.1 队列的介绍)
      • [2.2 queue的使用](#2.2 queue的使用)
      • [2.3 queue的模拟实现](#2.3 queue的模拟实现)
    • [3. 优先队列的介绍和使用](#3. 优先队列的介绍和使用)
      • [3.1 priority_queue的介绍](#3.1 priority_queue的介绍)
      • [3.2 priority_queue的使用](#3.2 priority_queue的使用)
      • [3.3 priority_queue的模拟实现](#3.3 priority_queue的模拟实现)
    • [4. 容器适配器](#4. 容器适配器)
      • [4.1 什么是适配器](#4.1 什么是适配器)
      • [4.2 STL标准库中stack和queue的底层结构](#4.2 STL标准库中stack和queue的底层结构)
      • [4.3 deque的简单介绍](#4.3 deque的简单介绍)
        • [4.3.1 deque的原理介绍](#4.3.1 deque的原理介绍)
        • [4.3.2 deque的缺陷](#4.3.2 deque的缺陷)
      • [4.4 为什么选择deque作为stack和queue的底层默认容器](#4.4 为什么选择deque作为stack和queue的底层默认容器)
      • [4.5 容器适配器设计的关键点](#4.5 容器适配器设计的关键点)
    • 总结

容器适配器深度解析:从STL的stack、queue到优先队列的底层实现

1. 栈的介绍和使用

1.1 栈的介绍

栈(stack)是一种后进先出(LIFO)的数据结构,它允许在一端进行插入和删除操作。在C++ STL中,stack是一个容器适配器,提供了一组特定的成员函数来访问其元素。

1.2 栈的使用

函数 接口说明
stack() 构造空的栈
empty() 检测stack是否为空
size() 返回stack中元素的个数
top() 返回栈顶元素的引用
push() 将元素val压入stack中
pop() 将stack中尾部的元素弹出

最小栈实现

最小栈能够在O(1)时间内获取栈中的最小元素。

cpp 复制代码
class MinStack {
public:
    void push(int x) {
        // 压栈时,先将元素保存到_elem
        _elem.push(x);
        // 如果x小于_min中栈顶的元素,将x再压入_min中
        if(_min.empty() || x <= _min.top()) 
            _min.push(x);
    }
    
    void pop() {
        // 如果_min栈顶的元素等于出栈的元素,_min顶的元素要移除
        if(_min.top() == _elem.top()) 
            _min.pop();
        _elem.pop();
    }
    
    int top() { return _elem.top(); }
    int getMin() { return _min.top(); }
    
private:
    std::stack<int> _elem;    // 保存栈中的元素
    std::stack<int> _min;     // 保存栈的最小值
};

栈的弹出压入序列

验证一个序列是否是栈的弹出序列。

cpp 复制代码
class MinStack {
public:
    MinStack() {
        
    }
    
    void push(int val) {
        st.push(val);
        if(minst.empty()||minst.top()>=val)
        {
            minst.push(val);
        }
    }
    
    void pop() {
        if(st.top()==minst.top())
        {
            minst.pop();
        }

        st.pop();
    }
    
    int top() {
        return st.top();
    }
    
    int getMin() {
        return minst.top();
        
    }

private:
    stack<int> st;
    stack<int> minst;
};

逆波兰表达式求值

cpp 复制代码
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> s;
        for(size_t i = 0; i < tokens.size(); ++i) {
            string& str = tokens[i];
            // str为数字
            if(!("+" == str || "-" == str || "*" == str || "/" == str)) {
                s.push(atoi(str.c_str()));
            } else { // str为操作符
                int right = s.top();
                s.pop();
                int left = s.top();
                s.pop();
                
                switch(str[0]) {
                    case '+': s.push(left + right); break;
                    case '-': s.push(left - right); break;
                    case '*': s.push(left * right); break;
                    case '/': s.push(left / right); break;
                }
            }
        }
        return s.top();
    }
};

1.3 stack的模拟实现

以下是使用容器适配器模式实现的stack,支持自定义底层容器(默认使用deque):

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

namespace bit
{
    // container适配器实现stack
    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()
        {
            return _con.empty();
        }
        
    private:
        Container _con;
    };
}

2. 队列的介绍和使用

2.1 队列的介绍

队列是一种先进先出(FIFO)的数据结构,元素从队尾入队列,从队头出队列。在C++ STL中,queue是一个容器适配器。

queue的特点:

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作
  2. 元素从队尾入队列,从队头出队列
  3. 底层容器可以是deque或list,默认使用deque

2.2 queue的使用

函数声明 接口说明
queue() 构造空的队列
empty() 检测队列是否为空
size() 返回队列中有效元素的个数
front() 返回队头元素的引用
back() 返回队尾元素的引用
push() 在队尾将元素val入队列
pop() 将队头元素出队列

2.3 queue的模拟实现

使用容器适配器模式实现的queue,支持自定义底层容器:

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()
        {
            return _con.empty();
        }
        
    private:
        Container _con;
    };
}

3. 优先队列的介绍和使用

3.1 priority_queue的介绍

优先队列是一种容器适配器,它的第一个元素总是它所包含的元素中最大的(默认大堆)。

priority_queue的特点:

  1. 第一个元素总是最大的(默认大堆)
  2. 类似于堆,可以随时插入元素,只能检索最大堆元素
  3. 默认使用vector作为底层容器
  4. 支持随机访问迭代器

3.2 priority_queue的使用

函数声明 接口说明
priority_queue() 构造一个空的优先级队列
priority_queue(first, last) 用迭代器范围构造优先级队列
empty() 检测优先级队列是否为空
top() 返回优先级队列中最大(最小)元素
push(x) 在优先级队列中插入元素x
pop() 删除优先级队列中最大(最小)元素

使用示例:

cpp 复制代码
#include<vector>
#include<queue>
#include<functional> // greater算法的头文件

void TestPriorityQueue() {
    // 默认情况下,创建的是大堆,其底层按照小于号比较
    vector<int> v{3,2,7,6,0,4,1,9,8,5};
    priority_queue<int> q1;
    for(auto& e : v) 
        q1.push(e);
    cout << q1.top() << endl; // 输出9
    
    // 创建小堆,将第三个模板参数换成greater比较方式
    priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
    cout << q2.top() << endl; // 输出0
}

注意:

  1. 默认情况下,priority_queue是大堆
  2. 如果要创建小堆,需要指定第三个模板参数为greater<T>
  3. 如果存放自定义类型,需要在自定义类型中提供><的重载

3.3 priority_queue的模拟实现

以下是完整的优先队列模拟实现,包含大堆和小堆支持:

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

namespace bit
{
    // 仿函数类:Less(升序/大堆)
    template<class T>
    class Less
    {
    public:
        bool operator()(const T& x, const T& y)
        {
            return x < y;
        }
    };

    // 仿函数类:Greater(降序/小堆)
    template<class T>
    class Greater
    {
    public:
        bool operator()(const T& x, const T& y)
        {
            return x > y;
        }
    };
    
    // 优先队列实现
    // 模板参数:
    // T - 元素类型
    // Container - 底层容器,默认vector<T>
    // Compare - 比较器,默认Less<T>(大堆)
    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)
        {
            int child = parent * 2 + 1;
            Compare com;
            
            while (child < _con.size())
            {
                // 找出较大(或较小)的孩子
                if(child + 1 < _con.size() && com(_con[child], _con[child + 1]))
                {
                    ++child;
                }
                
                // 如果父节点小于(或大于)孩子节点,交换
                if(com(_con[parent], _con[child]))
                {
                    swap(_con[child], _con[parent]);
                    parent = child;
                    child = parent * 2 + 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];
        }
        
        // 获取元素个数
        size_t size() const
        {
            return _con.size();
        }
        
        // 判断是否为空
        bool empty() const
        {
            return _con.empty();
        }
        
    private:
        Container _con;
    };
}

4. 容器适配器

4.1 什么是适配器

适配器是一种设计模式,将一个类的接口转换成客户希望的另外一个接口。

4.2 STL标准库中stack和queue的底层结构

stack和queue在STL中称为容器适配器,它们只是对其他容器的接口进行了包装。STL中stack和queue默认使用deque作为底层容器。

4.3 deque的简单介绍

4.3.1 deque的原理介绍

deque(双端队列)是一种双开口的"连续"空间的数据结构:

  • 可以在头尾两端进行插入和删除操作,时间复杂度为O(1)
  • 与vector比较,头插效率高,不需要搬移元素
  • 与list比较,空间利用率比较高

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际类似于一个动态的二维数组。

4.3.2 deque的缺陷
  • 不适合遍历,因为迭代器要频繁检测是否移动到小空间边界
  • 在实际中,需要线性结构时,大多数情况下优先考虑vector和list
  • deque的应用并不多,主要作为stack和queue的底层数据结构

4.4 为什么选择deque作为stack和queue的底层默认容器

  1. stack和queue不需要遍历(因此没有迭代器),只需要在固定的一端或两端操作
  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据)
  3. queue中的元素增长时,deque不仅效率高,而且内存使用率高

4.5 容器适配器设计的关键点

从上面的模拟实现可以看出容器适配器的核心设计思想:

  1. 模板参数灵活性
    • stack和queue都支持自定义底层容器
    • priority_queue支持自定义比较器,实现大堆/小堆切换
  2. 接口统一性
    • 所有适配器都提供push、pop、top/front/back、size、empty等标准接口
    • 隐藏底层容器的具体实现细节
  3. 代码复用
    • 通过组合已有的容器实现新功能
    • 减少代码重复,提高开发效率

总结

栈、队列和优先队列是三种基础且重要的数据结构,在C++ STL中通过容器适配器的方式实现。理解它们的底层原理、使用场景和实现方式,对于提高编程能力和解决实际问题都有重要意义。

通过模拟实现这些数据结构,我们可以:

  1. 深入理解容器适配器设计模式
  2. 掌握仿函数在泛型编程中的应用
  3. 理解堆算法的实现原理
  4. 学会如何设计灵活的、可扩展的数据结构

在实际开发中,根据具体需求选择合适的数据结构:

  • 需要后进先出操作时选择栈
  • 需要先进先出操作时选择队列
  • 需要快速获取最大/最小值时选择优先队列
相关推荐
进击的小头2 小时前
设计模式落地的避坑指南(C语言版)
c语言·开发语言·设计模式
ujainu2 小时前
Flutter + OpenHarmony 游戏开发进阶:虚拟摄像机系统——平滑跟随与坐标偏移
开发语言·flutter·游戏·swift·openharmony
乐迪信息2 小时前
乐迪信息:AI防爆摄像机的船舶船体烟火智能预警系统
大数据·网络·人工智能·算法·无人机
超绝振刀怪2 小时前
【C++ String】
c++·stl
金书世界2 小时前
使用PHP+html+MySQL实现用户的注册和登录(源码)
开发语言·mysql·php
蓝海星梦2 小时前
GRPO 算法演进——偏差修正/鲁棒优化/架构扩展篇
论文阅读·人工智能·深度学习·算法·自然语言处理·强化学习
小程同学>o<2 小时前
嵌入式之C/C++(四)预处理
c语言·c++·面试题库·嵌入式面试题
Dxy12393102162 小时前
MySQL如何避免隐式转换
开发语言·mysql
后来后来啊2 小时前
2026.2.2 & 2.3学习笔记
数据结构·笔记·学习·算法·leetcode