【STL】综述
文章目录
- 一、STL基本知识
- 概述
- 容器
- 二、序列式容器详述
- 数组容器array
- 向量容器vector
- 双端队列容器deque
- 链式容器list
- 正向链容器forward_list
- 二、关联式容器详述
- 红黑树RB-Tree
- 哈希表
- 参考博客
😊点此到文末惊喜↩︎
一、STL基本知识
概述
- STL六大组件(前三个是主要的)
- 容器(Containers):使用
类模板(class template)
实现的各种数据结构 - 算法(Algorithms):使用
函数模板(function template)
实现的各种常用算法 - 迭代器(Iterators):使用
类模板(class template)
通过重载指针操作函数实现遍历对象集合元素的泛型指针 - 仿函数(Functors):使用
重载operator()的class或class template
实现函数对象(将对象像函数一样调用) - 适配器(Adaptors):使用
类模板(class template)
通过修饰容器、仿函数接口或迭代器实现功能的转换 - 分配器(Allocators):使用
类模板(class template)
实现内存资源的管理
- 容器(Containers):使用
- 特点
- STL模板主要由算法、容器、迭代器三者组成,将数据和算法分离。算法通过迭代器操作容器存储的数据,其中迭代器和容器一一对应。
- STL主要依赖于模板思想,提供了足够的通用性,减少了对OOP的依赖。
容器
- 分类
- 序列式容器:按位置索引,逻辑结构为线性表
- 数组容器
array<T, N>
- 向量容器
vector<T>
- 双端队列容器
deque<T>
- 链式容器
list<T>
- 正向链容器
forward_list<T>
- 数组容器
- 关联式容器:按键值索引,逻辑结构为树
- set / multiset / unordered_set / unordered_multimap
- map / multimap / unordered_map / unordered_multimap
- 容器适配器:以序列式容器为底层构造的适配器,不是容器
- 栈
stack<T>
- 队列
queue<T>
- 优先队列
priority_queue <T>
- 栈
- 序列式容器:按位置索引,逻辑结构为线性表
集合 | 底层实现 | 是否有序 | 数值重复 | 更改数值 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
set | 红黑树 | 有序 | 否 | 否 | O(log n) | O(log n) |
multiset | 红黑树 | 无序 | 是 | 否 | O(log n) | O(log n) |
unordered_set | 哈希表 | 无序 | 否 | 否 | O(1) | O(1) |
unordered_multiset | 哈希表 | 无序 | 是 | 否 | O(1) | O(1) |
map | 红黑树 | 有序 | 不可重复 | 不可修改 | O(log n) | O(log n) |
multimap | 红黑树 | 有序 | 可重复 | 不可更改 | O(log n) | O(log n) |
unordered_map | 哈希表 | 无序 | 不可重复 | 不可更改 | O(1) | O(1) |
unordered_multimap | 哈希表 | 无序 | 可重复 | 不可更改 | O(1) | O(1) |
二、序列式容器详述
数组容器array
- 原理:在
普通数组
的基础上,增加了一些功能函数 - 特点
- 大小固定,无法进行动态的增删
- 可以进行随机访问或更改
- 功能函数的作用示例
- at(n):返回容器中n处位置元素的引用,该函数自动检查n是否在有效的范围内,如果不是则抛出out_of_range异常
向量容器vector
- 原理
- 底层为数组,使用三个迭代器(指针)表示,start表示容器首部位置,finish表示已使用末尾位置,end_of_storage表示整个容器的末尾位置(最大容量)
- 底层为数组,使用三个迭代器(指针)表示,start表示容器首部位置,finish表示已使用末尾位置,end_of_storage表示整个容器的末尾位置(最大容量)
- vector扩容
- 扩容原理
- 寻找新内存:内存中寻找一个与前一段空间相比
两倍
大小的空间作为扩充空间 - 拷贝旧数据:调用
拷贝构造函数
将原数据拷贝到新内存空间的前半段 - 释放旧内存:调用
析构函数
释放原内存空间
- 寻找新内存:内存中寻找一个与前一段空间相比
- 注意:一旦发生内存扩容,指向原空间的迭代器可能失效,即迭代器指针指向不变,但是内容变了,eg:erase()和insert()移动部分元素和扩容操作
- 相关函数
- push_back():未达最大容量,则直接在备用空间上建构元素,并调整迭代器finish,使vector变大。已达最大容量,则进行扩容。
- insert():未达到最大容量,则把当前要插入元素的位置后面的元素向后移动,然后把待插入元素插入到相应的位置。已达到最大容量进行扩容。
- 扩容原理
- 元素的访问
- operator[]:直接跳转位置访问元素,速度是很快的,时间复杂度为O(1)
- at():本质调用operator[],此外增加了越界检查
- 初始化
//定义具有10个整型元素的向量(尖括号为元素类型名,它可以是任何合法的数据类型),不具有初值,其值不确定 vector<int>a(10); //定义具有10个整型元素的向量,且给出的每个元素初值为1 vector<int>a(10,1); //用向量b给向量a赋值,a的值完全等价于b的值 vector<int>a(b); //将向量b中从0-2(共三个)的元素赋值给a,a的类型为int型 vector<int>a(b.begin(),b.begin()+3); //从数组中获得初值 int b[7]={1,2,3,4,5,6,7}; vector<int> a(b,b+7);
- 常用内置函数
#include<vector> vector<int> a,b; //b为向量,将b的0-2个元素赋值给向量a a.assign(b.begin(),b.begin()+3); //a含有4个值为2的元素 a.assign(4,2); //返回a的最后一个元素 a.back(); //返回a的第一个元素 a.front(); //返回a的第i元素,当且仅当a存在 a[i]; //清空a中的元素 a.clear(); //判断a是否为空,空则返回true,非空则返回false a.empty(); //删除a向量的最后一个元素 a.pop_back(); //删除a中第一个(从第0个算起)到第二个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)结束 a.erase(a.begin()+1,a.begin()+3); //在a的最后一个向量后插入一个元素,其值为5 a.push_back(5); //在a的第一个元素(从第0个算起)位置插入数值5, a.insert(a.begin()+1,5); //在a的第一个元素(从第0个算起)位置插入3个数,其值都为5 a.insert(a.begin()+1,3,5); //b为数组,在a的第一个元素(从第0个元素算起)的位置插入b的第三个元素到第5个元素(不包括b+6) a.insert(a.begin()+1,b+3,b+6); //返回a中元素的个数 a.size(); //返回a在内存中总共可以容纳的元素个数 a.capacity(); //将a的现有元素个数调整至10个,多则删,少则补,其值随机 a.resize(10); //将a的现有元素个数调整至10个,多则删,少则补,其值为2 a.resize(10,2); //将a的容量扩充至100, a.reserve(100); //b为向量,将a中的元素和b中的元素整体交换 a.swap(b); //b为向量,向量的比较操作还有 != >= > <= < a==b;
双端队列容器deque
- 原理:由一个
中控器
和多个缓冲区
组成,中控器中的每个节点指向一片连续的缓冲区,在逻辑上形成连续的双端队列// deque的迭代器数据结构 _Elt_pointer _M_cur; //用于保存迭代器当前位置 _Elt_pointer _M_first; //保存迭代器当前所属buffer的开始位置 _Elt_pointer _M_last;//保存迭代器当前所属buffer的结束位置 _Map_pointer _M_node; //用于保存迭代器当前所属的节点位置
- 特点
双端
进行插入和删除,时间复杂度为O(1),特别是头部插入比vector快。- 支持
随机访问
O(1),但顺序访问比vector慢,这是由deque数据结构决定的 - 中控器节点数量 = max(元素数量/512 + 2, 8),512是默认的每个buffer大小。
- 头端插入
push_front()
(尾部插入与该原理类似)- 头部buffer空间足够时,直接从后往前插入
- 头部buffer不足时
- 当中控器节点足够时,则申请一个头部buffer
- 当中控器节点不足时,重新申请一整块中控器节点内存,并将buffer地址进行浅拷贝
- 中间插入
insert()
- 检测插入位置
- 若在前半部分则从后向前移动1位
- 若在后半部分则从前向后移动1位
- 移动前,现在头部或尾部进行一次尝试插入,如果buffer不足则进行扩容,足够则插入。
- 检测插入位置
链式容器list
- 原理:底层数据结构为一个
双向循环链表
(SGI STL版本,有些只是用双向链表实现),相比双向链表只需要一个指针即可表示首尾元素(另一个通过指针的自增或自减获得)template<typename T,...> // 头节点 class list {//...//指向链表的头节点,并不存放数据__list_node<T>* node;//...以下还有list 容器的构造函数以及很多操作函数 } //链表节点数据结构 struct __List_node{//...__list_node<T>* prev;// prev 指针用于指向前一个节点__list_node<T>* next;// next 指针用于指向后一个节点T myval;// myval 用于存储当前元素的值//... }
- 特点
- 链表中的迭代器只能进行前移和后移操作,通过重载运算符实现的
- 插入删除比较方便,而且不会造成迭代器失效
- STL中list和vector是两个最长被用的容器
- 基本操作
size();//返回容器中元素的个数。
empty();//判断容器是否为空。
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除
resize(num);
//重新指定容器的长度为num,若容器变成,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
resize(num,elem);push_back(elem); //在容器尾部加入一个元素
pop_back(); //删除容器中最后一个元素
push_front(elem); //在容器开头插入一个元素
pop_front(); //从哪个容器开头移除第一个元素
insert(pos,elem); //在pos位置插elem元素的拷贝,返回新数据的位置
insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
clear(); //移除容器的所有数据
erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置
erase(pos); //删除pos位置的数据,返回下一个数据的位置
remove(elem); //删除容器中所有与elem值匹配的元素。
正向链容器forward_list
-
原理:底层数据结构为
单链表
,只能使用正向迭代器进行顺序遍历。 -
特点
- 因为只能通过自增前面元素的迭代器来到达序列的终点,所以back()、push_back()、pop_back()、emplace_back()也无法使用
- forward_list 的操作比 list 容器还要快,而且占用的内存更少
二、关联式容器详述
红黑树RB-Tree
- 定义
- 红黑树是一种自平衡的二叉查找树
- 左右子树高度差可能大于1(与平衡二叉树的差别)
- 每个节点具有红黑的颜色性质
- 每个结点的颜色必定是红色或黑色
- 根节点是黑色的
- 所有叶子节点都是黑色的NULL结点(除叶子节点每个节点都是具有两个孩子)
- 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
- 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点
- 结论
- 原理:颜色性质约束了红黑树的大致平衡,即
从根到叶子的最长的可能路径不多于最短的可能路径的两倍长
,在最坏的情况下也能保证O(log2N)O(log_2N)O(log2N)的时间复杂度 - 证明:最短的可能路径都是黑色结点,最长的可能路径有交替的红色和黑色结点。因为根据所有最长的路径都有相同数目的黑色结点,这就表明了没有路径能多于任何其他路径的两倍长。
- 原理:颜色性质约束了红黑树的大致平衡,即
- 特点
- 操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树
- 红黑树不会像二分搜索树一样退化为链表。
- 红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决
- 红黑树的旋转
- 原因:插入和删除操作可能导致无法维持红黑树的性质
- 基本旋转
- 左旋:左右左,也就是左旋是把该节点作为其右孩子的左孩子
- 右旋:右左右,也就是右旋就是把该节点作为其左孩子的右孩子
- 一般默认插入的节点为红色,只有出现连续的红色操作才会引发自平衡操作,如果出现
- 将插入节点的父节点变为黑色,持续向根节点进行变红的试探,如果不满足,再进行旋转操作
- 红黑树RBT和平衡二叉树ALV的比较
- 结构对比: RBT牺牲了高度平衡特性,换取插入和删除的引起不平衡后旋转操作的减少。
- 查找对比: AVL 查找时间复杂度最好,最坏情况都是O(logN),RBT在最坏情况实际略差。
- 插入删除对比: 插入和删除结点很容易造成树结构的不平衡,而RBT的平衡度要求较低。
- 当插入节点引起树的不平衡时,当插入一个结点都引起了树的不平衡,AVL和RBT都最多需要2次旋转操作。但在大量数据插入的情况下,RBT需要通过旋转变色操作来重新达到平衡的频度要小于AVL。
- 当删除节点引起树的不平衡时,AVL最多需要logN 次旋转操作,而RBT最多只需要3次。
- 应用
- 着名的Linux的的进程调度完全公平调度程序,用红黑树管理进程控制块,进程的虚拟内存区域都存储在一颗红黑树上,每个虚拟地址区域都对应红黑树的一个节点,左指针指向相邻的地址虚拟存储区域,右指针指向相邻的高地址虚拟地址空间。
- IO多路复用的epoll的的的实现采用红黑树组织管理的的的sockfd,以支持快速的增删改查。
- Nginx的的的中用红黑树管理定时器,因为红黑树是有序的,可以很快的得到距离当前最小的定时器。
哈希表
- 原理
- 在
关键字
和存储地址
间建立对应关系,使得元素的查找可以以O(1)的效率进
- 在
🚩点此跳转到首行↩︎
参考博客
- 详解 C++ STL 六大组件,看完不懂打我…
- 容器适配器stack,queue和priority_queue
- array容器
- [c++] c++ std::vector 底层实现机制
- 基于源码剖析vector实现原理及注意事项
- C++ STL笔记四:deque容器
- C++ STL list容器底层实现(详解版)
- STL之序列式容器(六)、forward_list容器
- 红黑树
- BST(二叉搜索树),AVL(平衡二叉树)、RBT(红黑树)的区别
- 红黑树变色和旋转,图文案例讲解
相关文章:

【STL】综述
STL,一文即可知 文章目录一、STL基本知识概述容器二、序列式容器详述数组容器array向量容器vector双端队列容器deque链式容器list正向链容器forward_list二、关联式容器详述红黑树RB-Tree哈希表参考博客😊点此到文末惊喜↩︎ 一、STL基本知识 概述 STL…...
C++中编译的静态库与动态库
1.什么是库库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载…...
JS对象到原始值的转换
JS对象到原始值转换的复杂性 主要由于某些对象类型存在不止一种原始值的表示 对象到原始值转换的三种基本算法 在解释三种算法前需要了解toString valueOf这两个方法 toString 返回对象的字符串表示Array类的toString方法会将每个元素转换为字符串,再使用逗号作为…...

深度复盘-重启 etcd 引发的异常
作者信息: 唐聪、王超凡,腾讯云原生产品中心技术专家,负责腾讯云大规模 TKE 集群和 etcd 控制面稳定性、性能和成本优化工作。 王子勇,腾讯云专家级工程师, 腾讯云计算产品技术服务专家团队负责人。 概况 作为当前中国…...
2023年春招热点面试题(一)------新特性
文章目录一、Spring 6.0 新特性二、Spring Boot 3.0 新特性三、JDK 系列 新特性A.**JDK8新特性(2014年初)(LTS版本)**B. **JDK9新特性(2017年9月)**C.**JDK10新特性(2018年3月)**D.*…...
工程项目管理系统源码+spring cloud 系统管理+java 系统设置+二次开发
工程项目各模块及其功能点清单 一、系统管理 1、数据字典:实现对数据字典标签的增删改查操作 2、编码管理:实现对系统编码的增删改查操作 3、用户管理:管理和查看用户角色 4、菜单管理:实现对系统菜单的增删改查操…...

想要精通算法和SQL的成长之路 - 接雨水
想要精通算法和SQL的成长之路 - 接雨水前言一. 接雨水前言 想要精通算法和SQL的成长之路 - 系列导航 一. 接雨水 原题链接 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 输入:height [0,…...

Vue3 更高效的构建工具——Vite
文章目录前言一、Vite简介1. Vite组成2.为什么选 Vite?二、Vite的优缺点vite优点vite缺点三、使用Vite创建Vue3项目1. 创建 vite 的项目2.项目的结构前言 本文讲解了构建工具 Vite,目前只有vue3才可以使用Vite,如果本文对你有所帮助请三连支持博主。 下…...

优思学院|從《狂飙》高启强爱看的《孙子兵法》到六西格玛项目管理
近期最受人瞩目的,无疑是电视剧《狂飙》中出类拔萃的反派高启强。而在剧中,指引高启强走向顶峰的,正是那部著名的军事经典——《孙子兵法》。 在剧中,高启强在一次村庄改造项目上遇到了困难,但他仍保持冷静࿰…...
如何利用状态机编程实现启保停控制(含Stateflow模型介绍)
状态机的介绍这里不再赘述,概念也很简单没有过多的复杂理论。下面我们直接给出具体实现过程。有限自动状态机详细讲解请参看下面的文章链接: PLC面向对象编程系列之有限状态机(FSM)详解_RXXW_Dor的博客-CSDN博客_有限状态机 plc实现编写PLC控制机器动作类程序时,当分支比较…...

4. sql 语句中常用命令
1. 数据表: 本文中所有命令,测试的数据表结构如下图: 2. 查询语句: 2.1 基础查询:select //查询单个字段: select 字段名 from 表名; //查询多个字段 select 字段名1,字段名2,... from 表名; //查询所…...

第三章 Opencv图像像素操作
目录1.像素1-1.确定像素位置1-2.获取指定像素的像素值1-3.修改像素的BGR值2.用numpy模块操作像素2-1.创建图像1.创建黑白图像2.创建彩色图像3.创建随机图像2-2.拼接图像1.水平拼接hstack()方法2.垂直拼接vstack()方法1.像素 1.像素是构成数字图像的最小单位。每一幅图像都是由M…...

SpringBoot集成swagger3(CD2207)(内含教学视频+源代码)
SpringBoot集成swagger3(CD2207)(内含教学视频源代码) 教学视频源代码下载链接地址:https://download.csdn.net/download/weixin_46411355/87435564 目录SpringBoot集成swagger3(CD2207)&#…...
Go语言语言学习十三(反射的对象值)
在Go语言中反射不仅可以获取值的类型和种类,还可以获取值和更改值,使用reflect.ValueOf()获取和设置变量的值。 使用反射值包装任意值 Go语言通过reflect.ValueOf()获取的是值的反射值对象,书写格式如下 value : reflect.ValueOf(rawValue…...
【ESP 保姆级教程】玩转emqx数据集成篇② ——控制台输出动作(多用于测试环境调试功能)
忘记过去,超越自己 ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-02-10 ❤️❤️ 本篇更新记录 2023-02-10 ❤️🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请…...

MyBatis案例 | 使用映射配置文件实现CRUD操作——添加数据
本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等,如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址:🔥JavaWeb Java入门篇: 🔥Java基础学习篇 Java进阶学习篇&…...

2023年,什么样的CRM,才是您最需要的?
春节假期刚刚结束,当大家还沉浸在新春佳节的喜悦中时,很多地方已经争先恐后地奋力开跑了。近日,全国各地方政府相继出台并发布了2023年数字化转型规划,纷纷结合自身的区位特色和优势资源,明确2023年乃至此后数年的数字…...
【C语言】编程初学者入门训练(6)
文章目录51. 计算一元二次方程52. 获取月份天数53. 简单计算器54. 线段图案55. 正方形图案56. 直角三角形图案57. 翻转直角三角形图案58. 带空格直角三角形图案59. 金字塔图案60. 翻转金字塔图案51. 计算一元二次方程 问题描述:从键盘输入a, b, c的值,编…...

Java笔记-异常相关
一、异常概述与异常体系结构 Error:Java虚拟机无法解决的严重问题: JVM系统内部错误,资源耗尽,如:StackOverflow \OOM堆栈溢出 处理办法:只能修改代码,不能编写处理异常的代码 Exception:可以处理的异常 &…...
pytest-xdist测试用例并发
官方文档:pytest-xdist初次使用参考:Python测试框架pytest(22)插件 - pytest-xdist(分布式执行)pytest测试框架系列 - Pytest pytest-xdist 分布式、多进程并发执行用例你会用吗?Pytest-xdist并…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...

恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
ThreadLocal 源码
ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物,因为每个访问一个线程局部变量的线程(通过其 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,这些类希望将…...
32位寻址与64位寻址
32位寻址与64位寻址 32位寻址是什么? 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元(地址),其核心含义与能力如下: 1. 核心定义 地址位宽:CPU或内存控制器用32位…...