<C++> STL_容器适配器
1.容器适配器
适配器是一种设计模式,该种模式是将一个类的接口转换成客户希望的另外一个接口。
容器适配器是STL中的一种重要组件,用于提供不同的数据结构接口,以满足特定的需求和限制。容器适配器是基于其他STL容器构建的,通过改变其接口和行为,使其适应不同的应用场景。
STL中有三种常见的容器适配器:stack、queue和priority_queue。
虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:


而priority_queue默认使用vector

2.stack的介绍和使用
stack的介绍
-
stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
-
stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
-
stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:
-
empty:判空操作
-
back:获取尾部元素操作
-
push_back:尾部插入元素操作
-
pop_back:尾部删除元素操作
-
-
标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque。

stack的使用
当你使用 std::stack 时,你可以使用以下一些重要的成员函数:
push(const T& value): 将元素value压入栈顶。pop(): 弹出栈顶元素,不返回其值。top(): 返回栈顶元素的引用,但不将其从栈中移除。size(): 返回栈中元素的数量。empty(): 检查栈是否为空,返回布尔值。swap(stack& other): 将当前栈与另一个栈other进行交换。
#include <iostream>
#include <stack>int main() {std::stack<int> myStack;// 将元素压入栈顶myStack.push(10);myStack.push(20);myStack.push(30);std::cout << "栈顶元素: " << myStack.top() << std::endl; // 输出:30std::cout << "栈的大小: " << myStack.size() << std::endl; // 输出:3myStack.pop(); // 弹出栈顶元素if (!myStack.empty()) {std::cout << "弹出元素后的栈顶元素: " << myStack.top() << std::endl; // 输出:20}std::stack<int> anotherStack;anotherStack.push(50);myStack.swap(anotherStack); // 交换两个栈的内容std::cout << "交换后 myStack 的大小: " << myStack.size() << std::endl; // 输出:1return 0;
}
stack的模拟实现
#pragma once
#include <deque>namespace phw {template<class T, class Container = std::deque<T>>class stack {public:void push(const T &x) {_con.push_back(x);}void pop() {_con.pop_back();}T &top() {return _con.back();}const T &top() const {return _con.back();}size_t size() const {return _con.size();}bool empty() const {return _con.empty();}void swap(stack<T, Container> &st) {_con.swap(st._con);}private:Container _con;};
}// namespace phw
3.queue的介绍和使用
queue的介绍
-
队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端 提取元素。
-
队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的 成员函数来访问其元素。元素从队尾入队列,从队头出队列。
-
底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
-
empty:检测队列是否为空
-
size:返回队列中有效元素的个数
-
front:返回队头元素的引用
-
back:返回队尾元素的引用
-
push_back:在队列尾部入队列
-
pop_front:在队列头部出队列
-
-
标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标 准容器deque。
queue的使用
push(const T& value): 将元素value添加到队列的尾部。pop(): 弹出队列的头部元素,不返回其值。front(): 返回队列头部元素的引用。back(): 返回队列尾部元素的引用。size(): 返回队列中元素的数量。empty(): 检查队列是否为空。swap(queue& other): 交换两个队列的内容。
示例:
#include <iostream>
#include <queue>int main() {std::queue<int> myQueue;// 向队列尾部添加元素myQueue.push(10);myQueue.push(20);myQueue.push(30);std::cout << "队列头部元素: " << myQueue.front() << std::endl; // 输出:10std::cout << "队列尾部元素: " << myQueue.back() << std::endl; // 输出:30std::cout << "队列大小: " << myQueue.size() << std::endl; // 输出:3myQueue.pop(); // 弹出队列头部元素std::cout << "弹出元素后的队列头部元素: " << myQueue.front() << std::endl; // 输出:20std::queue<int> anotherQueue;anotherQueue.push(50);myQueue.swap(anotherQueue); // 交换两个队列的内容std::cout << "交换后 myQueue 的大小: " << myQueue.size() << std::endl; // 输出:1return 0;
}
queue的模拟实现
#pragma once
#include <deque>namespace phw//防止命名冲突
{template<class T, class Container = std::deque<T>>class queue {public://队尾入队列void push(const T &x) {_con.push_back(x);}//队头出队列void pop() {_con.pop_front();}//获取队头元素T &front() {return _con.front();}const T &front() const {return _con.front();}//获取队尾元素T &back() {return _con.back();}const T &back() const {return _con.back();}//获取队列中有效元素个数size_t size() const {return _con.size();}//判断队列是否为空bool empty() const {return _con.empty();}//交换两个队列中的数据void swap(queue<T, Container> &q) {_con.swap(q._con);}private:Container _con;};
}// namespace phw
4.priority_queue的介绍和使用
priority_queue的介绍
std::priority_queue 用于实现优先队列数据结构。优先队列是一种特殊的队列,其中的元素按照一定的优先级顺序进行排列,而不是按照插入的顺序。
std::priority_queue 使用堆(heap)数据结构来维护元素的顺序,通常用于需要按照一定规则获取最高优先级元素的场景。在默认情况下,std::priority_queue 会以降序排列元素,即最大的元素会被放置在队列的前面。你也可以通过提供自定义的比较函数来改变排序顺序。
priority_queue的使用
构造函数
默认构造函数: 创建一个空的优先队列。
std::priority_queue<T> pq; // 创建一个空的优先队列,T 是元素类型
带有比较函数的构造函数: 可以指定一个自定义的比较函数,用于确定元素的优先级顺序。
std::priority_queue<T, Container, Compare> pq;
T是元素类型。Container是底层容器类型,默认情况下使用std::vector。Compare是比较函数类型,用于确定元素的顺序。默认情况下,使用operator<来比较元素。
示例:
#include <iostream>
#include <queue>int main() {// 构造一个默认的优先队列,元素类型为 int,使用默认比较函数 operator<std::priority_queue<int> pq1;// 构造一个优先队列,元素类型为 int,使用自定义的比较函数 greater,实现最小堆std::priority_queue<int, std::vector<int>, std::greater<int>> pq2;// 构造一个优先队列,元素类型为 std::string,使用默认比较函数 operator<std::priority_queue<std::string> pq3;// 构造一个优先队列,元素类型为 double,使用 lambda 表达式作为比较函数,实现最大堆auto cmp = [](double a, double b) { return a < b; };std::priority_queue<double, std::vector<double>, decltype(cmp)> pq4(cmp);return 0;
}
成员函数
-
push(): 向优先队列中插入元素。
pq.push(value); // 插入元素 value -
pop(): 移除队列顶部的元素(即最高优先级元素)。
pq.pop(); // 移除队列顶部元素 -
top(): 获取队列顶部的元素(即最高优先级元素)。
T highestPriority = pq.top(); // 获取最高优先级元素 -
empty(): 检查队列是否为空。
bool isEmpty = pq.empty(); // 如果队列为空则返回 true,否则返回 false -
size(): 获取队列中的元素数量。
int numElements = pq.size(); // 获取队列中的元素数量
示例:
#include <iostream>
#include <functional>
#include <queue>
using namespace std;
int main(){priority_queue<int> q;q.push(3);q.push(6);q.push(0);q.push(2);q.push(9);q.push(8);q.push(1);while (!q.empty()){cout << q.top() << " ";q.pop();}cout << endl; //9 8 6 3 2 1 0return 0;
}
自定义比较函数
你可以通过提供自定义的比较函数来改变元素的排序顺序。比较函数应该返回 true,如果第一个参数应该排在第二个参数之前。
struct MyComparator {bool operator()(const T& a, const T& b) {// 自定义比较逻辑}
};std::priority_queue<T, Container, MyComparator> pq;
priority_queue的模拟实现
priority_queue的底层实际上就是堆结构,实现priority_queue之前,我们先认识两个重要的堆算法。(下面这两种算法我们均以大堆为例)
堆的向上调整算法

以大堆为例,堆的向上调整算法就是在大堆的末尾插入一个数据后,经过一系列的调整,使其仍然是一个大堆。
调整的基本思想如下:
1、将目标结点与其父结点进行比较。
2、若目标结点的值比父结点的值大,则交换目标结点与其父结点的位置,并将原目标结点的父结点当作新的目标结点继续进行向上调整;若目标结点的值比其父结点的值小,则停止向上调整,此时该树已经是大堆了。
例如,现在我们在该大堆的末尾插入数据88。

我们先将88与其父结点54进行比较,发现88比其父结点大,则交换父子结点的数据,并继续进行向上调整。

此时将88与其父结点87进行比较,发现88还是比其父结点大,则继续交换父子结点的数据,并继续进行向上调整。

这时再将88与其父结点89进行比较,发现88比其父结点小,则停止向上调整,此时该树已经就是大堆了。

堆的向上调整算法代码:
void AdjustUp(int child) {int parent = (child - 1) / 2;while (child > 0) {// 默认less ,也就是parent<childif (_comp(_con[parent], _con[child]))// 通过所给比较方式确定是否需要交换结点位置{// 将父结点与孩子结点交换swap(_con[child], _con[parent]);// 继续向上进行调整child = parent;parent = (child - 1) / 2;} else// 已成堆{break;}}
}
堆的向下调整算法
以大堆为例,使用堆的向下调整算法有一个前提,就是待向下调整的结点的左子树和右子树必须都为大堆。

调整的基本思想如下:
1、将目标结点与其较大的子结点进行比较。
2、若目标结点的值比其较大的子结点的值小,则交换目标结点与其较大的子结点的位置,并将原目标结点的较大子结点当作新的目标结点继续进行向下调整;若目标结点的值比其较大子结点的值大,则停止向下调整,此时该树已经是大堆了。
例如,将该二叉树从根结点开始进行向下调整。(此时根结点的左右子树已经是大堆)

将60与其较大的子结点88进行比较,发现60比其较大的子结点小,则交换这两个结点的数据,并继续进行向下调整。

此时再将60与其较大的子结点87进行比较,发现60比其较大的子结点小,则再交换这两个结点的数据,并继续进行向下调整。

这时再将60与其较大的子结点54进行比较,发现60比其较大的子结点大,则停止向下调整,此时该树已经就是大堆了。

堆的向下调整算法代码:
void AdjustDown(int n, int parent) {int child = 2 * parent + 1;while (child < n) {//_comp(_con[child], _con[child + 1])表示child<child+1if (child + 1 < n && _comp(_con[child], _con[child + 1])) {child++;}// parent<childif (_comp(_con[parent], _con[child]))// 通过所给比较方式确定是否需要交换结点位置{// 将父结点与孩子结点交换swap(_con[child], _con[parent]);// 继续向下进行调整parent = child;child = 2 * parent + 1;} else// 已成堆{break;}}
}
模拟实现代码:
// 优先级队列使用vector作为其底层存储数据的容器,priority_queue就是堆
// 默认情况是大堆#include <iostream>
#include <vector>
using namespace std;
namespace phw {// 仿函数less(使内部结构为大堆)template<class T>struct less {bool operator()(const T &x, const T &y) {return x < y;}};// 仿函数greater(使内部结构为小堆)template<class T>struct greater {bool operator()(const T &x, const T &y) {return x > y;}};// 优先级队列template<class T, class Container = vector<T>, class Compare = greater<T>>class priority_queue {public:// 堆的向上调整void AdjustUp(int child) {int parent = (child - 1) / 2;while (child > 0) {// 默认less ,也就是parent<childif (_comp(_con[parent], _con[child]))// 通过所给比较方式确定是否需要交换结点位置{// 将父结点与孩子结点交换swap(_con[child], _con[parent]);// 继续向上进行调整child = parent;parent = (child - 1) / 2;} else// 已成堆{break;}}}// 堆的向下调整void AdjustDown(int n, int parent) {int child = 2 * parent + 1;while (child < n) {//_comp(_con[child], _con[child + 1])表示child<child+1if (child + 1 < n && _comp(_con[child], _con[child + 1])) {child++;}// parent<childif (_comp(_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(_con.size(), 0);// 将第0个元素进行一次向下调整}// 访问队头元素(堆顶元素)T &top() {return _con[0];}const T &top() const {return _con[0];}// 获取队列中有效元素个数size_t size() const {return _con.size();}// 判断队列是否为空bool empty() const {return _con.empty();}private:Container _con;// vector容器Compare _comp; // 比较方式};
}// namespace phw
(_con[0], _con[_con.size() - 1]);_con.pop_back();AdjustDown(_con.size(), 0);// 将第0个元素进行一次向下调整}// 访问队头元素(堆顶元素)T &top() {return _con[0];}const T &top() const {return _con[0];}// 获取队列中有效元素个数size_t size() const {return _con.size();}// 判断队列是否为空bool empty() const {return _con.empty();}private:Container _con;// vector容器Compare _comp; // 比较方式};
}// namespace phw
相关文章:
<C++> STL_容器适配器
1.容器适配器 适配器是一种设计模式,该种模式是将一个类的接口转换成客户希望的另外一个接口。 容器适配器是STL中的一种重要组件,用于提供不同的数据结构接口,以满足特定的需求和限制。容器适配器是基于其他STL容器构建的,通过…...
【25考研】- 整体规划及高数一起步
【25考研】- 整体规划及高数一起步 一、整体规划二、专业课870计算机应用基础参考网上考研学长学姐: 三、高数一典型题目、易错点及常用结论(一)典型题目(二)易错点(三)常用结论1.令tarctanx, 则…...
【Unity】常见的角色移动旋转
在Unity 3D游戏引擎中,可以使用不同的方式对物体进行旋转。以下是几种常见的旋转方式: 欧拉角(Euler Angles):欧拉角是一种常用的旋转表示方法,通过绕物体的 X、Y 和 Z 轴的旋转角度来描述物体的旋转。在Un…...
今天的小结
1、冒泡排序 冒泡排序(Bubble Sort)是一种简单的排序算法,它重复地遍历待排序的元素列表,比较相邻的元素并交换它们的位置,直到整个列表排序完成。冒泡排序的基本思想是通过不断交换相邻元素,将最大&#…...
了解 Socks 协议:它的过去、现在与未来
在网络世界的江湖中,有一名叫做 Socks 协议的高手,它凭借着一招“代理”绝技,在网络安全领域独步天下。今天,就让我们来了解一下这位神秘高手的过去、现在和未来。 在过去,互联网世界的江湖可谓是风起云涌,…...
小谈静态类和单例模式
静态类(Static Class)和单例(Singleton)都是在编程中用于实现特定类型的设计模式或代码组织方式。它们在不同的情境下有不同的用途和特点。 静态类(Static Class) 静态类是一种类,它的方法和属…...
LeetCode解法汇总823. 带因子的二叉树
目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 描述: 给出一个含…...
TypeScript的变量声明的各种方式
TypeScript是一种静态类型的JavaScript超集,它为JavaScript代码提供了类型检查和更好的代码组织结构。在TypeScript中,变量声明是非常重要的,因为它们定义了变量的类型和范围。本文将详细介绍TypeScript的变量声明,并通过代码案例…...
c++ lambda
Lambda Lambda 表达式一般用于定义匿名函数,使得代码更加灵活简洁,优点: 声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序,好的可读性和可…...
泊松回归和地理加权泊松回归
01 泊松回归 泊松回归(Poisson Regression)是一种广义线性模型,用于建立离散型响应变量(计数数据)与一个或多个预测变量之间的关系。它以法国数学家西蒙丹尼泊松(Simon Denis Poisson)的名字命名,适用于计算“事件发生次数”的概率,比如交通事故发生次数、产品缺陷数…...
【数学建模竞赛】各类题型及解题方案
评价类赛题建模流程及总结 建模步骤 建立评价指标->评价体系->同向化处理(都越多越好或越少越少)->指标无量纲处理 ->权重-> 主客观->合成 主客观评价问题的区别 主客观概念主要是在指标定权时来划分的。主观评价与客观评价的区别…...
【12期】谈一谈redis两种持久化机制的区别?
Redis两类持续性的方法 RDB方案可以在规定时间间隔内创建数据集的时间点快照。 AOF方案记录了服务器执行的所有写操作命令,并在服务器启动时通过重新执行这些命令来还原数据集。AOF文件完全遵循Redis协议格式保存,新命令会被追加到文件末尾。此外&#…...
Lambda 编程(Kotlin)一
学习记录,以下为个人理解 知识点: Lambda的定义:允许你把代码块当作参数传递给函数Lambda的语法约定:如果lambda 表达式是函数调用的最后一个实参,它可以放到括号的外边当lambda表达式时函数唯一的实参时,…...
网络字节序——TCP接口及其实现简单TCP服务器
网络字节序——TCP接口及其实现简单TCP服务器 文章目录 网络字节序——TCP接口及其实现简单TCP服务器简单TCP服务器的实现1. 单进程版:客户端串行版2. 多进程版:客户端并行版netstat查看网络信息3.多线程版:并行执行log.hpp 守护进程fg、bg s…...
RxJS如何根据事件创建Observable对象?
RxJS提供了一些用来创建Observable对象的函数,我们可以根据事件、定时器、Promise,AJAX等来创建Observable对象。 下面是根据事件创建Observable对象的几个例子。 this.outsideClick$ fromEvent(document, click).subscribe((event: MouseEvent) > …...
网站常见安全漏洞 | 青训营
Powered by:NEFU AB-IN 文章目录 网站常见安全漏洞 | 青训营 网站基本组成及漏洞定义服务端漏洞SQL注入命令执行越权漏洞SSRF文件上传漏洞 客户端漏洞开放重定向XSSCSRF点击劫持CORS跨域配置错误WebSocket 网站常见安全漏洞 | 青训营 网站常见安全漏洞-网站基本组成及漏洞定义…...
vue2使用 vis-network 和 vue-vis-network 插件封装一个公用的关联关系图
效果图: vis组件库:vis.js vis-network中文文档:vis-network 安装组件库: npm install vis-network vue-vis-network 或 yarn add vis-network vue-vis-network 新建RelationGraph.vue文件: <template><…...
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。
LeetCode第73题矩阵置零 1.思路: 想到一个开辟一点空间来解决方法,使用哈希集。就是使用一个哈希集(row和col)来储存数组中的元素为0的下标。然后再遍历,整个二维数组,在哈希集中存在对应的下标,…...
java-初识Servlet,Tomcat,JDBC
文章目录 前言一、ServletServlet 生命周期Servlet 实例Servlet 过滤器 二、TomcatJDBCJDBC连接数据库实例 总结 前言 java入门须知的重要概念/名词/技术 等 一、Servlet Servlet是Java Web开发中的一个核心组件,它是基于Java语言编写的服务器端程序,…...
SpringBoot+mybatis+pgsql多个数据源配置
一、配置文件 jdk环境:1.8 配置了双数据源springbootdruidpgsql,application.properties配置修改如下: #当前入库主数据库 spring.primary.datasource.typecom.alibaba.druid.pool.DruidDataSource spring.primary.datasource.driver-class…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
