C++ 栈(Stack)与队列(Queue)深度解析:从原理到实战

article/2025/6/20 19:38:42

一、栈(Stack):后进先出(LIFO)的线性结构

1. 核心特性与应用场景

  • 特性:仅允许在栈顶进行元素的插入(push)和删除(pop)操作,遵循 “后进先出” 原则。
  • 典型应用
    • 函数调用栈:记录函数调用顺序与局部变量状态。
    • 表达式求值:如逆波兰表达式(后缀表达式)计算。
    • 括号匹配:检测代码中括号是否成对出现。

2. C++ 标准库 stack 使用指南

2.1 头文件与命名空间
#include <stack>
using namespace std;//或者std::stack<int> st;
2.2 常用接口
接口说明
stack<T>构造空栈
push(x)将元素 x 压入栈顶
pop()弹出栈顶元素(不返回值)
top()返回栈顶元素引用
empty()检测栈是否为空(返回布尔值)
size()返回栈中元素个数

二、队列(Queue):先进先出(FIFO)的线性结构

1. 核心特性与应用场景

  • 特性:元素从队尾入队(push),从队头出队(pop),遵循 “先进先出” 原则。
  • 典型应用
    • 任务调度:如打印机任务队列、操作系统进程调度。
    • 广度优先搜索(BFS):用于遍历图或树结构。
    • 消息缓冲:网络请求、事件处理中的消息队列。

2. C++ 标准库 queue 使用指南

2.1 头文件与命名空间
#include <queue>
using namespace std;
2.2 常用接口
接口说明
queue<T>构造空队列
push(x)将元素 x 入队(队尾)
pop()弹出队头元素(不返回值)
front()返回队头元素引用
back()返回队尾元素引用
empty()检测队列是否为空
size()返回队列中元素个数
2.3 示例代码:用队列实现栈
class MyStack {
private:queue<int> q1, q2; // 使用两个队列模拟栈
public:void push(int x) {q1.push(x); // 新元素始终加入非空队列}int pop() {if (q1.empty()) swap(q1, q2); // 切换队列while (q1.size() > 1) {q2.push(q1.front()); // 转移元素,保留最后一个(栈顶)q1.pop();}int val = q1.front();q1.pop();return val;}int top() { return q1.empty() ? q2.back() : q1.back(); }
};

三、优先队列(Priority Queue):基于堆的动态优先级结构

1. 核心特性与应用场景

  • 特性:每个元素有优先级,每次取出优先级最高(或最低)的元素,底层基于堆实现。
  • 典型应用
    • 堆排序:利用优先队列实现高效排序。
    • 任务调度:操作系统中按优先级分配资源。
    • 图算法:Dijkstra 算法求最短路径、Prim 算法求最小生成树。

2. C++ 标准库 priority_queue 使用指南

2.1 头文件与命名空间
#include <queue>
using namespace std;
2.2 常用接口
接口说明
priority_queue<T>构造大顶堆(默认)
push(x)插入元素 x 并调整堆结构
pop()弹出堆顶元素(最大 / 最小值)
top()返回堆顶元素引用
empty()检测优先队列是否为空
size()返回元素个数

四、容器适配器:stack 和 queue 的底层实现

1. 适配器模式简介

  • 定义:将一个类的接口转换为另一种接口,使原本不兼容的类可以协同工作。
  • STL 中的应用stack 和 queue 本质是容器适配器,通过封装其他容器(如 dequevector)的接口实现特定功能。

2. 为什么选择 deque 作为默认底层容器?

  • deque 的优势
    • 双端操作高效:头插 / 头删、尾插 / 尾删均为 O (1) 时间复杂度。
    • 内存利用率高:相比 list,连续存储(分段连续)减少指针开销。
    • 避免 vector 的缺陷:扩容时无需搬移大量元素,适合频繁插入 / 删除场景。
  • 适配性stack 仅需尾插 / 尾删,queue 需尾插 / 头删,deque 完美满足需求。

3. deque 的本质与逻辑结构

3.1 分段连续的存储空间

deque 是一种 双开口的 “连续” 空间数据结构,但实际并非物理连续,而是由 一段段连续的小空间(缓冲区)拼接而成,整体呈现 “逻辑连续” 的假象 。

  • 每个缓冲区(buffer)通常是固定大小的数组(如 8 或 16 字节)。

  • 缓冲区之间通过 中控器(map) 管理,中控器是一个指针数组,每个元素指向一个缓冲区的起始地址 。

  • 逻辑示意图

     
    • 中控器维护缓冲区的地址,形成 “动态二维数组” 结构。
    • 通过这种设计,deque 既具备类似 vector 的随机访问能力,又支持高效的双端操作。
3.2 迭代器的复杂性

deque 的迭代器需要维护 “逻辑连续” 的假象,因此设计复杂。每个迭代器包含以下信息 :

  • cur:当前指针,指向缓冲区中的具体元素。

  • first:当前缓冲区的起始地址。

  • last:当前缓冲区的结束地址(不包含)。

  • node:指向中控器中当前缓冲区对应的指针(即 map 中的元素)。

  • 迭代器移动逻辑

    • 当 cur 指向当前缓冲区的末尾时,迭代器通过 node 切换到下一个缓冲区,并将 cur 指向新缓冲区的起始位置,反之亦然。
    • 这种机制使得 deque 的迭代器可以像 vector 一样进行 ++/-- 操作,但实际会涉及缓冲区的切换 。

    4.deque 的核心特性

    4.1 双端操作的高效性
    • 头插 / 头删(O (1) 时间复杂度)
      • 无需像 vector 一样搬移大量元素,只需在头部缓冲区添加 / 删除元素,或新建 / 销毁一个缓冲区(极端情况) 。
    • 尾插 / 尾删(O(1) 时间复杂度)
      • 与 vector 类似,直接操作尾部缓冲区,仅在缓冲区满时新建一个缓冲区 。
    4.2 与vector、list 的对比
    特性dequevectorlist
    内存结构分段连续(逻辑连续)物理连续非连续(链表)
    头插 / 头删效率O (1)(无需搬移元素)O (n)(需搬移全部元素)O(1)
    随机访问效率O (1)(通过迭代器切换缓冲区)O (1)(直接指针运算)O (n)(需遍历链表)
    空间利用率高(缓冲区紧凑,无额外指针)高(连续存储)低(每个节点含前驱 / 后继指针)
    典型场景双端操作频繁(如 stack/queue 底层)随机访问频繁(如数组下标操作)任意位置插入 / 删除频繁
    4.3 致命缺陷:遍历效率低
    • deque 的迭代器在遍历时需要频繁检测是否到达缓冲区边界,导致效率低于 vector 的原生指针遍历。因此,deque 不适合高频遍历场景 。
    • 高效场景:栈(push_back/pop_back)、队列(push_back/pop_front)。
    • 低效场景:多次调用 begin() 到 end() 的遍历(如 for (auto it = d.begin(); it != d.end(); ++it))。

    5.deque 作为 stack/queue 底层容器的原因

    5.1 适配性分析
    • stack 需求:仅需 push_back/pop_back,deque 的尾操作与 vector 等价,但扩容时无需搬移元素(效率更高) 。
    • queue 需求:需 push_back/pop_front,deque 的头删操作(pop_front)效率优于 vectorvector 头删需搬移所有元素),且空间利用率优于 list 。
    5.2 deque 的设计完美契合 stack/queue 的接口需求
    • 不需要遍历(无迭代器需求),避开遍历低效的缺陷。
    • 双端操作高效,内存使用紧凑。
    • STL 中 stack/queue 的默认底层容器为 deque,而非 vector 或 list 。

    6.对比分析

    知识点知识点
    deque 的逻辑结构分段连续空间,通过中控器管理缓冲区,呈现 “逻辑连续” 假象
    迭代器设计头插 / 头删效率优于 vector,空间利用率优于 list
    双端操作优势迭代器需频繁检测缓冲区边界,遍历效率低,不适合高频遍历场景
    遍历缺陷迭代器需频繁检测缓冲区边界,遍历效率低,不适合高频遍历场景
    作为 stack/queue 底层双端操作高效 无需遍历,完美适配 stack/queue 的接口需求

    总结:deque 的核心价值

    deque 的设计是 双端操作高效性 与 空间利用率 的平衡:

    • 优势场景:需要频繁双端操作(如栈、队列),但无需高频遍历的数据结构。
    • 局限性:遍历效率低,因此 STL 中 algorithm 算法优先推荐使用 vector 或 list

    六、优先队列中的仿函数(Functor)应用

    1. 仿函数基础概念

    • 定义:仿函数是一种可调用对象,本质是重载了 operator() 的类或结构体。
    • 作用:在优先队列中,通过仿函数自定义元素的比较规则,实现大顶堆或小顶堆的灵活切换。

    2. 仿函数在优先队列中的使用场景

    优先队列默认使用 大顶堆(通过 < 运算符比较元素),若需创建 小顶堆,需显式指定仿函数 greater<T>。例如:

    #include <queue>
    #include <functional> // 包含 greater 仿函数void TestPriorityQueue() {vector<int> v = {3, 1, 4, 2};// 默认大顶堆(使用 less<int> 仿函数,可省略不写)priority_queue<int> max_heap;for (int x : v) max_heap.push(x); // 堆顶为 4// 显式使用 greater<int> 仿函数创建小顶堆priority_queue<int, vector<int>, greater<int>> min_heap(v.begin(), v.end());// 堆顶为 1(最小值)
    }
    

    3. 自定义仿函数:处理自定义类型

    当优先队列存储自定义类型时,需通过仿函数或重载运算符定义比较规则。以下是通过独立仿函数实现相同功能的方式:

    class Date {
    public:int _year, _month, _day;Date(int y, int m, int d) : _year(y), _month(m), _day(d) {}
    };// 自定义仿函数:大顶堆(按日期从大到小排序)
    struct DateGreater {bool operator()(const Date& d1, const Date& d2) const {if (d1._year != d2._year) return d1._year > d2._year;if (d1._month != d2._month) return d1._month > d2._month;return d1._day > d2._day;}
    };// 自定义仿函数:小顶堆(按日期从小到大排序)
    struct DateLess {bool operator()(const Date& d1, const Date& d2) const {if (d1._year != d2._year) return d1._year < d2._year;if (d1._month != d2._month) return d1._month < d2._month;return d1._day < d2._day;}
    };// 使用示例
    void TestCustomFunctor() {vector<Date> dates = {Date(2023, 10, 5),Date(2023, 10, 3),Date(2023, 11, 1)};// 大顶堆:优先取出最新日期priority_queue<Date, vector<Date>, DateGreater> max_heap(dates.begin(), dates.end());cout << max_heap.top()._year << "-" << max_heap.top()._month << "-" << max_heap.top()._day << endl; // 输出:2023-11-1// 小顶堆:优先取出最旧日期priority_queue<Date, vector<Date>, DateLess> min_heap(dates.begin(), dates.end());cout << min_heap.top()._year << "-" << min_heap.top()._month << "-" << min_heap.top()._day << endl; // 输出:2023-10-3
    }
    

    4. 仿函数与运算符重载的选择

    • 场景一:若自定义类型可自然排序(如 Date 类的日期顺序),建议重载 operator<(对应大顶堆)或 operator>(对应小顶堆),保持代码简洁。
    • 场景二:若需灵活切换多种排序规则(如有时按年份、有时按月日排序),使用独立仿函数更灵活,避免污染类的运算符定义。

    5. 关键细节

    • 默认情况下,priority_queue 是大堆,底层通过 less<T> 仿函数实现。
    • 若需小堆,需显式指定第三个模板参数为 greater<T> 或自定义仿函数,如:
      priority_queue<int, vector<int>, greater<int>> min_heap; // 小顶堆
      
    • 自定义仿函数的返回值必须为 bool,且遵循 严格弱序关系(strict weak ordering),确保堆结构正确维护。

    总结:仿函数的核心作用

    灵活控制排序规则:通过仿函数,优先队列可轻松切换大顶堆 / 小顶堆,或为自定义类型定制比较逻辑。

    七、栈和队列的模拟实现

    1. 栈的模拟实现(基于 deque

    1.1 类模板定义与模板参数
    namespace My_stack {template<class T, class Con = std::deque<T>>class stack { /* ... */ };
    }
    
    • 模板参数
      • T:栈中存储元素的类型(必填)。
      • Con:底层容器类型(可选,默认 std::deque<T>),文档中提到 stack 本质是容器适配器,可封装任意支持 push_back/pop_back/back 接口的容器(如 vectorlist) 。
    • 命名空间
      My_stack 避免与标准库 std::stack 命名冲突,符合自定义组件的封装规范。
    1.2 构造函数
    stack() {}
    
    • 功能:默认构造空栈,底层容器 _c 调用 Con 的默认构造函数(如 deque 的空构造)。
    • 复杂度:O (1),仅初始化底层容器。
    1.3 核心接口实现
    1.3.1 压栈操作:push(const T& x)
    void push(const T& x) { _c.push_back(x); }
    
    • 原理:调用底层容器的 push_back 接口,在容器尾部添加元素,符合栈的 “后进先出” 特性 。
    • 示例:若 Con 为 deque,元素被添加到双端队列的尾部,时间复杂度 O (1)(均摊)。
    1.3.2 弹栈操作:pop()
    void pop() { _c.pop_back(); }
    
    • 原理:调用底层容器的 pop_back 接口,移除尾部元素(即栈顶元素),不返回值 。
    • 注意:若栈为空时调用 pop(),会导致未定义行为(需提前通过 empty() 检测)。
    1.3.3 获取栈顶元素:top()
    T& top() { return _c.back(); }
    const T& top()const { return _c.back(); }
    
    • 重载版本
      • 非 const 版本:返回栈顶元素的可修改引用。
      • const 版本:返回栈顶元素的常量引用,用于 const stack 对象。
    • 底层调用:通过容器的 back() 接口获取尾部元素,时间复杂度 O (1) 。
    1.3.4 辅助接口:size() 和 empty()
    size_t size()const { return _c.size(); }
    bool empty()const { return _c.empty(); }
    

    功能

    • size():返回栈中元素个数,调用容器的 size() 接口。
    • empty():检测栈是否为空,调用容器的 empty() 接口,等价于 size() == 0 。
    • 复杂度:均为 O (1),直接转发底层容器的实现。
    1.4 底层容器选择:默认使用 deque 的原因
    class stack { template<class T, class Con = std::deque<T>> /* ... */ }
    
    • 文档依据
      STL 中 stack 的默认底层容器为 deque,因其双端操作高效且内存利用率高。
      • deque 的 push_back/pop_back 均为 O (1) 操作,且扩容时无需像 vector 一样搬移大量元素 。
      • 对于栈而言,无需遍历功能,完美避开 deque 遍历效率低的缺陷 。
    • 可替代性
      若显式指定 Con 为 vector 或 list,需确保容器支持以下接口:
      push_back(const T&)  // 压栈
      pop_back()           // 弹栈
      back() const&        // 获取栈顶
      size() const         // 元素个数
      empty() const        // 判空
      
    1.5.示例用法
    #include<iostream>
    using namespace std;int main() {My_stack::stack<int> s; // 使用默认 deque 作为底层容器s.push(10);s.push(20);cout << "栈顶元素:" << s.top() << endl; // 输出 20s.pop();cout << "栈大小:" << s.size() << endl; // 输出 1cout << "是否为空:" << boolalpha << s.empty() << endl; // 输出 falsereturn 0;
    }
    

     2. 队列的模拟实现(基于 deque

    namespace My_Queue
    {template<class T, class Con = std::deque<T>>class queue{public:queue(){ }void push(const T& x){_c.push_back(x);}void pop(){_c.pop_front();}T& back(){return _c.back();}const T& back()const{return _c.back();}T& front(){return _c.front();}const T& front()const{return _c.front();}size_t size()const{return _c.size();}bool empty()const{return _c.empty();}private:Con _c;};
    };

    3. 优先队列的模拟实现(基于 vector 和堆算法)

    3.1 类模板定义与模板参数
    template<class T, class container=std::vector<T>, class compare=less<T>>
    class priority_queue { /* ... */ };
    
    • 模板参数
      • T:优先队列中存储元素的类型(必填)。
      • container:底层容器类型(可选,默认 std::vector<T>),需支持随机访问(如 vectordeque),文档提到优先队列底层容器需满足 push_back/pop_back/front 等接口 。
      • compare:比较仿函数(可选,默认 less<T>),用于定义元素优先级。less<T> 表示大顶堆(默认),greater<T> 表示小顶堆 。
    • 设计意图:通过模板参数实现容器和比较规则的灵活配置,符合文档中 “优先队列是容器适配器” 的定位 。
    3.2 核心接口实现
    3.2.1 构造函数:priority_queue()
    priority_queue() { }
    
    • 功能:默认构造空优先队列,底层容器 _con 调用 container 的默认构造函数(如 vector 的空构造)。
    • 复杂度:O (1),仅初始化底层容器。
    3.3.2 插入元素:push(const T& val)
    void push(const T& val) {_con.push_back(val); // 尾部添加元素Adjustup(_con.size()-1); // 向上调整堆,维持堆性质
    }
    
    • 核心逻辑
      1. 新元素添加到容器尾部(对应堆的最后一个位置)。
      2. 调用 Adjustup 从下往上调整堆,确保父节点优先级高于子节点(大顶堆场景)。
    • Adjustup 算法
      • 比较当前节点(child)与父节点(parent),若父节点优先级低于子节点(com(_con[parent], _con[child]) 为真),则交换两者位置,直至堆性质满足 。
      • 时间复杂度:O (log n),其中 n 为堆中元素个数。
    void Adjustup(size_t 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;}}
    }
    3.3.3 删除堆顶元素:pop()
    void pop() {swap(_con[0], _con[size() - 1]); // 交换堆顶与最后一个元素_con.pop_back(); // 删除最后一个元素(原堆顶)Adjustdown(0); // 向下调整堆,维持堆性质
    }
    
    • 核心逻辑
      1. 将堆顶元素(最大 / 最小值)与堆尾元素交换,使堆顶元素位于容器尾部。
      2. 删除尾部元素(即原堆顶),此时堆顶为新元素,可能破坏堆性质。
      3. 调用 Adjustdown 从上往下调整堆,重新确定新堆顶的位置。
    • Adjustdown 算法
      • 比较当前节点(parent)与左右子节点(child),选择优先级最高的子节点交换位置,直至堆性质满足 。
      • 时间复杂度:O(log n)。
    void Adjustdown(size_t parent)
    {compare com;size_t child = 2 * parent + 1;size_t size = size();while (child < size){if (child + 1 < 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;}}
    }
    3.3.4 获取堆顶元素:top()
    const T& top() { return _con[0]; }
    
    • 功能:返回堆顶元素的常量引用(大顶堆为最大值,小顶堆为最小值)。
    • 前提条件:队列非空,否则访问 _con[0] 会导致未定义行为(需提前通过 empty() 检测)。
    • 底层实现:直接返回容器首元素,时间复杂度 O (1),符合文档中 “优先队列只能检索堆顶元素” 的特性 。
    3.3.5 辅助接口:size() 和 empty()
    size_t size() { return _con.size(); }
    bool empty() { return _con.empty(); }
    
    • 功能
      • size():返回队列中元素个数,调用容器的 size() 接口。
      • empty():检测队列是否为空,等价于 size() == 0
    • 复杂度:均为 O (1),直接转发底层容器的实现。

    4.底层容器与比较仿函数

    4.1 底层容器 container
    • 默认选择 vector 的原因
      • vector 支持随机访问(通过下标 []),满足堆算法(Adjustup/Adjustdown)的索引需求。
      • push_back/pop_back 操作高效,且扩容时通过复制实现,保证数据连续性 。
    • 可替代性
      若指定为 deque,需确保其支持随机访问(deque 符合要求),但实际优先队列更常用 vector 作为底层容器(文档默认场景) 。
    4.2 比较仿函数 compare
    • 默认 less<T>(大顶堆)
      compare com; // 实例化仿函数
      if (com(_con[parent], _con[child])) { // 若 parent < child(大顶堆逻辑)swap(_con[child], _con[parent]); // 父节点与子节点交换,使父节点更大
      }
      
    • 切换为小顶堆(greater<T>
      只需将模板参数 compare 改为 greater<T>,此时 com(a, b) 判断 a > b 是否成立,调整堆时确保父节点更小。
    • 自定义仿函数
      参考文档中 Date 类的比较逻辑,可自定义仿函数实现特定优先级规则,如:
      struct MyCompare {bool operator()(const int& a, const int& b) {return a % 10 < b % 10; // 按个位数大小排序}
      };
      priority_queue<int, vector<int>, MyCompare> pq; // 使用自定义比较规则
      

    5.示例用法(大顶堆)

    #include<iostream>
    using namespace std;int main() {priority_queue<int> pq; // 默认大顶堆,底层 vector,比较仿函数 less<int>pq.push(30);pq.push(10);pq.push(20);cout << "堆顶元素(最大值):" << pq.top() << endl; // 输出 30pq.pop();cout << "新堆顶元素:" << pq.top() << endl; // 输出 20return 0;
    }
    

    八、经典算法题实战

     1. 最小栈

    class MinStack {
    private:stack<int> data;       // 存储所有元素stack<int> min_data;   // 存储最小值
    public:void push(int x) {data.push(x);if (min_data.empty() || x <= min_data.top()) {min_data.push(x);}}void pop() {if (data.top() == min_data.top()) {min_data.pop();}data.pop();}int top() { return data.top(); }int getMin() { return min_data.top(); }
    };
    

    2. 栈的压入、弹出序列

    题目:给定入栈序列 pushV 和出栈序列 popV,判断 popV 是否为 pushV 的有效弹出序列。
    思路:用栈模拟入栈和出栈过程,逐个匹配出栈元素。
    代码

    bool IsPopOrder(vector<int> pushV, vector<int> popV) {if (pushV.size() != popV.size()) return false;stack<int> s;int i = 0; // 入栈指针for (int num : popV) {// 栈顶元素不等于当前出栈元素时,继续入栈while (s.empty() || s.top() != num) {if (i >= pushV.size()) return false; // 无元素可入栈s.push(pushV[i++]);}s.pop(); // 匹配成功,弹出栈顶}return true;
    }
    

    3. 逆波兰表达式求值

    题目:根据逆波兰表达式(后缀表达式)计算结果,运算符包括 +-*/
    思路:用栈存储操作数,遇到运算符时弹出栈顶两个元素计算结果并压栈。
    代码

    int evalRPN(vector<string>& tokens) {stack<int> sv;for(const auto& e:tokens){if(e=="+"||e=="-"||e=="*"||e=="/"){int a=sv.top();sv.pop();int b=sv.top();sv.pop();switch(e[0]){case '/':sv.push(b/a);break;case '*':sv.push(a*b);break;case '-':sv.push(b-a);break;case '+':sv.push(a+b);break;}}else{sv.push(stoi(e));}}return sv.top();
    }

    六、总结与拓展建议

    1. 核心对比

    数据结构访问规则底层容器(STL 默认)典型场景
    stack后进先出(LIFO)deque函数调用、表达式求值
    queue先进先出(FIFO)dequeBFS、任务队列
    priority_queue优先级优先vector(堆结构)堆排序、最短路径算法

    2. 拓展学习建议

    • 底层原理:深入理解 deque 的分段连续存储结构与迭代器设计。
    • 算法应用:练习用栈实现回溯算法(如括号生成),用队列实现层序遍历。
    • 自定义类型:在 priority_queue 中使用自定义类时,需重载比较运算符(< 或 >)。

    通过本文的理论解析与实战代码,相信你已掌握 C++ 中栈与队列的核心概念与应用。在实际开发中,根据场景选择合适的数据结构,能显著提升代码效率与可读性。

    附录

    模拟实现

    //stack.h
    #include<iostream>
    #include<deque>namespace My_stack
    {template<class T, class Con = std::deque<T>>class stack{public:stack(){}void push(const T& x){_c.push_back(x);}void pop(){_c.pop_back();}T& top(){return _c.back();}const T& top()const{return _c.back();}size_t size()const{return _c.size();}bool empty()const{return _c.empty();}private:Con _c;};
    };
    //queue.h
    namespace My_Queue
    {template<class T, class Con = std::deque<T>>class queue{public:queue(){ }void push(const T& x){_c.push_back(x);}void pop(){_c.pop_front();}T& back(){return _c.back();}const T& back()const{return _c.back();}T& front(){return _c.front();}const T& front()const{return _c.front();}size_t size()const{return _c.size();}bool empty()const{return _c.empty();}private:Con _c;};template<class T>class less{public:bool operator()(const T& a, const T& b){return a < b;}};template<class T>class greater{public:bool operator()(const T& a, const T& b){return a > b;}};template<class T,class container=std::vector<T>,class compare=less<T>>class priority_queue{public:priority_queue(){ }void push(const T& val){_con.push_back(val);Adjustup(_con.size()-1);}void pop(){swap(_con[0], _con[size() - 1]);_con.push_back();Adjustdown(0);}const T& top(){return _con[0];}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:void Adjustup(size_t 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(size_t parent){compare com;size_t child = 2 * parent + 1;size_t size = size();while (child < size){if (child + 1 < 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;}}}private:container _con;};
    };


    http://www.hkcw.cn/article/eCCPmnwDwx.shtml

    相关文章

    【C++高级主题】命令空间(五):类、命名空间和作用域

    目录 一、实参相关的查找&#xff08;ADL&#xff09;&#xff1a;函数调用的 “智能搜索” 1.1 ADL 的核心规则 1.2 ADL 的触发条件 1.3 ADL 的典型应用场景 1.4 ADL 的潜在风险与规避 二、隐式友元声明&#xff1a;类与命名空间的 “私密通道” 2.1 友元声明的基本规则…

    Python Day38 学习

    继续昨日的内容浙大疏锦行 学习一下两种机制&#xff1a;try-except机制和try-except-else-finally机制 try-except 摘自讲义 try&#xff1a;把你认为可能会出错的代码放在这里。 except&#xff1a;如果 try 块里的代码真的出错了&#xff08;从出错开始就不会继续执行t…

    linux 1.0.7

    用户和权限的含义与作用 linux中的用户和文件 用户的权限是非常重要的 而且有些程序需要使用管理员身份去执行 这些都是非常重要的 不可能让所有的人拥有所有的权限 这样的工具可以避免非法的手段来修改计算机中的数据 linux之所以安全还是权限管理做的很棒 每个登录的用户都有…

    BFD 基本工作原理与实践:如何与 VRRP 联动实现高效链路故障检测?

    BFD 基本工作原理与实践&#xff1a;如何与 VRRP 联动实现高效链路故障检测&#xff1f; &#x1f310; BFD 的基本原理BFD 主要特点BFD 工作机制 &#x1f500; 为什么 VRRP 需要 BFD&#xff1f;&#x1f527; BFD VRRP 配置实战&#xff08;华为设备&#xff09;&#x1f4…

    python中将一个列表样式的字符串转换成真正列表的办法以及json.dumps()和 json.loads()

    今天学习python的web.py&#xff0c;返回的内容为列表样式的字符串&#xff0c;如下 string_data "[(13.212.95.888, 8000, 10), (13.212.95.999, 8000, 10)]" 此时&#xff0c;如果想提取第一个元素&#xff0c;也就是(13.212.95.888, 8000, 10)&#xff0c;不能…

    C++:指针(Pointers)

    目录 什么是指针&#xff1f; 为什么需要指针&#xff1f; 1. 访问堆&#xff08;Access Heap&#xff09; 2. 资源管理&#xff08;Resource Management&#xff09; 3. 参数传递&#xff08;Parameter Passing&#xff09; 如何声明和使用指针&#xff1f; 如何利用指…

    Acrobat DC v25.001 最新专业版已破,像word一样编辑PDF!

    在数字化时代&#xff0c;PDF文件以其稳定性和通用性成为了文档交流和存储的热门选择。无论是阅读、编辑、转换还是转曲&#xff0c;大家对PDF文件的操作需求日益增加。因此&#xff0c;一款出色的PDF处理软件不仅要满足多样化的需求&#xff0c;还要通过简洁的界面和强大的功能…

    RabbitMQ 高级特性

    准备工作 1. 创建 Spring 项目 2. 引入依赖 3.修改配置文件 RabbitMQ官网 AMQP 0-9-1 Protocol Extensions | RabbitMQ 消息确认 消息确认机制 生产者发送消息,到达消费者后,可能会有以下情况: 1.消息处理成功 2.消息处理异常 RabbitMQ 向消费者发送消息之后,会把消息删除…

    机器学习:欠拟合、过拟合、正则化

    本文目录&#xff1a; 一、欠拟合二、过拟合三、拟合问题原因及解决办法四、正则化&#xff1a;尽量减少高次幂特征的影响&#xff08;一&#xff09;L1正则化&#xff08;二&#xff09;L2正则化&#xff08;三&#xff09;L1正则化与L2正则化的对比 五、正好拟合代码&#xf…

    电路学习(二)之电容

    电容的基本功能是通交流隔直流、存储电量&#xff0c;在电路中可以进行滤波、充放电。 1.什么是电容&#xff1f; &#xff08;1&#xff09;电容定义&#xff1a;电容器代表了器件存储电荷的能力&#xff0c;通俗来理解是两块不连通的导体与绝缘的中间体组成。当给电容充电时…

    第十二节:第二部分:集合框架:Collection集合的遍历方式:迭代器、增强for循环、Lambda、案例

    迭代器遍历集合 增强for循环遍历集合 Lambda表达式遍历集合 代码&#xff1a; 代码一&#xff1a;使用迭代器遍历集合 package com.itheima.day18_Collection;import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; // //使用迭代器遍历集合…

    任务18:时间序列的模型

    任务描述 知识点&#xff1a; 移动平均法指数平滑法ARIMA模型 重 点&#xff1a; 指数平滑法ARIMA模型 内 容&#xff1a; 创建时间序列索引绘制时间序列图形处理时间序列数据建立时间序列模型模型效果评估应用模型预测 任务指导 1. 移动平均法 移动平均法&#xff…

    Java研学-MongoDB(一)

    一 MongoDB 简介 MongoDB是一种高性能、开源的NoSQL数据库&#xff0c;采用面向文档的存储模型&#xff0c;以BSON&#xff08;Binary JSON&#xff09;格式存储数据&#xff0c;具有灵活的数据模型、强大的扩展性和丰富的功能特性&#xff0c;广泛应用于各类现代应用程序的数据…

    【LLM相关知识点】 LLM关键技术简单拆解,以及常用应用框架整理(二)

    【LLM相关知识点】 LLM关键技术简单拆解&#xff0c;以及常用应用框架整理&#xff08;二&#xff09; 文章目录 【LLM相关知识点】 LLM关键技术简单拆解&#xff0c;以及常用应用框架整理&#xff08;二&#xff09;一、市场调研&#xff1a;业界智能问答助手的标杆案例1、技术…

    自动化立体仓库WCS的设计与实现

    导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。欢迎大家使用我们的仓储物流技术AI智能体。 新书《智能物流系统构成与技术实践》 新书《智能仓储项目出海-英语手册&#xff0c;必备&#xff01;》 完整版文件和更多学习资料&#xf…

    2025年5月18日蓝桥stema省选拔赛编程题答案解析

    题目&#xff1a;水龙头 时间限制&#xff1a;C/C 语言 1000MS&#xff1b;其他语言 3000MS 内存限制&#xff1a;C/C 语言 65536KB&#xff1b;其他语言 589824KB 题目描述&#xff1a; 小明在 0 时刻&#xff08;初始时刻&#xff09;将一个空桶放置在漏水的水龙头下。已知桶…

    基于开源AI大模型AI智能名片S2B2C商城小程序源码的销售环节数字化实现路径研究

    摘要&#xff1a;在数字化浪潮下&#xff0c;企业销售环节的转型升级已成为提升竞争力的核心命题。本文基于清华大学全球产业研究院《中国企业数字化转型研究报告&#xff08;2020&#xff09;》提出的“提升销售率与利润率、打通客户数据、强化营销协同、构建全景用户画像、助…

    使用 HTML + jsmind 实现在线思维导图

    在日常工作和学习中&#xff0c;思维导图是一种非常有效的可视化工具&#xff0c;可以帮助我们梳理思路、规划任务、整理知识结构。本文将带你一步步了解如何使用 HTML 和 jsmind 实现一个基础的在线思维导图应用。 效果演示 项目概述 本项目主要包含以下核心功能&#xff1a…

    利用python工具you-get下载网页的视频文件

    有时候我们可能在一个网站看到一个视频&#xff08;比如B站&#xff09;&#xff0c;想下载&#xff0c;但是页面没有下载视频的按钮。这时候&#xff0c;我们可以借助python工具you-get来实现下载功能。下面简要说下步骤 &#xff08;一&#xff09;因为使用的是python工具&a…

    threejs渲染器和前端UI界面

    1. three.js Canvas画布布局 学习本节课之前&#xff0c;可以先回顾下第一章节入门部分的6和12两小节关于threejs Canvas画布布局的讲解。 网页上局部特定尺寸&#xff1a;1.6 第一个3D案例—渲染器(opens new window) 全屏&#xff0c;随窗口变化:1.12 Canvas画布布局和全屏…