C/C++ STL

news2024/11/24 11:02:03

常见容器性质总结

1.vector 底层数据结构为数组 ,支持快速随机访问
2.list 底层数据结构为双向链表,支持快速增删
3.deque 底层数据结构为一个中央控制器和多个缓冲区,详细见STL源码剖析P146,支持首尾(中间
不能)快速增删,也支持随机访问
deque是一个双端队列(double-ended queue),也是在堆中保存内容的.它的保存形式如下:
[堆1] --> [堆2] -->[堆3] --> …
每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品.
4.stack 底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容
耗时
5.queue 底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩
容耗时(stack和queue其实是适配器,而不叫容器,因为是对容器的再封装)
6.priority_queue 的底层数据结构一般为vector为底层容器,堆heap为处理规则来管理底层容器实现
7.set 底层数据结构为红黑树,有序,不重复
8.multiset 底层数据结构为红黑树,有序,可重复
9.map 底层数据结构为红黑树,有序,不重复
10.multimap 底层数据结构为红黑树,有序,可重复
11.unordered_set 底层数据结构为hash表,无序,不重复
12.unordered_multiset 底层数据结构为hash表,无序,可重复
13.unordered_map 底层数据结构为hash表,无序,不重复
14.unordered_multimap 底层数据结构为hash表,无序,可重复

每种容器对应的迭代器

容器迭代器
vector、deque随机访问迭代器
stack、queue、priority_queue
list、(multi)set/map双向迭代器
unordered_(multi)set/map、forward_list前向迭代器

vector

vector数据结构

vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。因此能高效的进行随机存取,时间复杂度为o(1);但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(n)。另外,当数组中内存空间不够时,会重新申请一块内存空间并进行内存拷贝。连续存储结构:vector是可以实现动态增长的对象数组,支持对数组高效率的访问和在数组尾端的删除和插入操作,在中间和头部删除和插入相对不易,需要挪动大量的数据。它与数组最大的区别就是vector不需程序员自己去考虑容量问题,库里面本身已经实现了容量的动态增长,而数组需要程序员手动写入扩容函数进形扩容。vector中有三个指针first,last,end, 分别指向了数组的开头, 数组的结尾+1以及数组的最大容量结尾
在这里插入图片描述

vector扩容

● 可以使用reserve(n)预先分配一块较大的指定大小的内存空间,这样当指定大小的内存空间未使用完时,是不会重新分配内存空间的,这样便提升了效率。只有当n>capacity()时,调用reserve(n)才会改变vector容量。
● resize()成员函数只改变元素的数目,不改变vector的容量。
● vector以连续的数组存放数据,当vector空间已满时会申请新的空间并将原容器中的内容拷贝到新空间中,并销毁原容器
● 存储空间的重新分配会导致迭代器失效
● 因为分配空间后需要进行拷贝,编译器会预分配更多空间以减少发生拷贝影响程序效率
● 扩容的大小叫做扩容因子,扩容因子由编译器决定,VS的扩容因子为1.5,G++中,扩容因子为2

为什么是2倍扩容

理由一:
倍数方式空间拷贝数据次数

假设总共有n个元素,以m倍的形式增长。(比如现在举例n=100, m=2),所以,vector的push_back的操作次数可以是logmN,也就是log2(100),换算下来大概是需要进行7次扩容。这样的话,相当于旧空间数据到原空间数据的拷贝有7次。

个数方式空间拷贝数据次数

和倍数方式假设相同,n=100;这次m代表的是每次新空间的大小位n+m;m为新空间新增大小,比如这次m为10(每次新增10个空间)。所以这次的扩容次数为 100/10 = 10次,也就是说,插入100白个元素,需要扩容10次。

但是,如果n=1000的情况下, 以个数形式进行扩容就不能在为10了,否则拷贝空间次数将会太多
有的小伙伴要问:但是可以取100呀,想想,如果n=10的情况下,取100又不太合适,所以,以个数的形式来进行扩容显然不符合所用n的取值。
所以在STL中vector以倍数的形式进行扩容

理由二:
如果以大于2倍的方式来进行扩容,下一次申请空间会大于之前申请所有空间的总和,这样会导致之前的空间不能再被重复利用,这样是很浪费空间的操作。所以,如果扩容一般基于(1, 2] 之间进行扩容

vector缩容

首先vector删除元素时会不会释放空间,erase()函数,只能删除内容,不能改变容量大小;
由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留
下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。
empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空
间依然如故,无法保证内存的回收。
如果需要空间动态缩小,可以考虑使用deque。如果vector,可以用swap()来帮助你释放内存

vector(Vec).swap(Vec); 将Vec的内存空洞清除; 
vector().swap(Vec); 清空Vec的内存;

std::vector<T> tmp(v);
tmp.swap(v);

vector<int>(v).swap(v);  //关键的内存收缩语句

vector与list中sort的区别

vector中内存是连续分配的,而list是基于双向链表的。所以sort的时候,vector的sort是基于快排的,而list的sort是基于归并排序的

vector中下标越界访问

通过下标访问vector中的元素时不会做边界检查,即便下标越界。
也就是说,下标与first迭代器相加的结果超过了finish迭代器的位置,程序也不会报错,而是返回这个地
址中存储的值。
如果想在访问vector中的元素时首先进行边界检查,可以使用vector中的at函数。通过使用at函数不但
可以通过下标访问vector中的元素,而且在at函数内部会对下标进行边界检查。

push_back和emplace_back的区别

使用push_back()函数需要调用拷贝构造函数和转移构造
函数,而使用emplace_back()插入的元素原地构造,不需要触发拷贝构造和转移构造

list

  1. list不再能够像vector一样以普通指针作为迭代器,因为其节点不保证在存储空间中连续存在;
  2. list插入操作和结合才做都不会造成原有的list迭代器失效;
  3. list不仅是一个双向链表,而且还是一个环状双向链表,所以它只需要一个指针;
  4. list不像vector那样有可能在空间不足时做重新配置、数据移动的操作,所以插入前的所有迭代器在插
    入操作之后都仍然有效;
  5. deque是一种双向开口的连续线性空间,所谓双向开口,意思是可以在头尾两端分别做元素的插入和
    删除操作;可以在头尾两端分别做元素的插入和删除操作;
  6. deque和vector最大的差异,一在于deque允许常数时间内对起头端进行元素的插入或移除操作,二
    在于deque没有所谓容量概念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间
    并链接起来,deque没有所谓的空间保留功能

map,set

他们的底层都是以红黑树的结构实现,因此插入删除等操作都在O(logn)时间内完成,因此可以完成高
效的插入删除;

红黑树是怎么能够同时实现这两种容器

在这里我们定义了一个模版参数,如果它是key那么它就是set,如果它是map,那么它就是map;底
层是红黑树,实现map的红黑树的节点数据类型是key+value,而实现set的节点数据类型是value

为什么使用红黑树

因为map和set要求是自动排序的,红黑树能够实现这一功能,而且时间复杂度比较低

unordered_map(hash_map)和map的区别

  1. unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。不同的是
    unordered_map不会根据key的大小进行排序,
  2. 存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的,而map中的
    元素是按照二叉搜索树存储,进行中序遍历会得到有序遍历。
  3. 所以使用时map的key需要定义operator<。而unordered_map需要定义hash_value函数并且重载
    operator==。但是很多系统内置的数据类型都自带这些,
  4. 那么如果是自定义类型,那么就需要自己重载operator<或者hash_value()了。
  5. 如果需要内部元素自动排序,使用map,不需要排序使用unordered_map
  6. unordered_map的底层实现是hash_table;
  7. hash_map底层使用的是hash_table,而hash_table使用的开链法进行冲突避免,所有hash_map采
    用开链法进行冲突解决。
  8. 什么时候扩容:当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值—即当前
    数组的长度乘以加载因子的值的时候,就要自动扩容啦。
  9. 扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组
    无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。

unordered_map内部是一个hash_table,是由一个大vector,vector的每个元素节点挂一个链表来实现的(就是开链法实现的哈希桶)

在这里插入图片描述
hashtable中的bucket所维护的list既不是list也不是slist,而是其自己定义的由hashtable_node数据结
构组成的linked-list,而bucket聚合体本身使用vector进行存储。hashtable的迭代器只提供前进操作,
不提供后退操作
在hashtable设计bucket的数量上,其内置了28个质数[53, 97, 193,…,429496729],在创建hashtable
时,会根据存入的元素个数选择大于等于元素个数的质数作为hashtable的容量(vector的长度),其中
每个bucket所维护的linked-list长度也等于hashtable的容量。如果插入hashtable的元素个数超过了
bucket的容量,就要进行重建table操作,即找出下一个质数,创建新的buckets vector,重新计算元素
在新hashtable的位置。

map越界访问

map的下标运算符[]的作用是:将key作为下标去执行查找,并返回相应的值;如果不存在这个key,
就将一个具有该key和value的某人值插入这个map。

map中[]与find的区别?

  1. map的下标运算符[]的作用是:将关键码作为下标去执行查找,并返回对应的值;如果不存在这个关
    键码,就将一个具有该关键码和值类型的默认值的项插入这个map。
  2. map的find函数:用关键码执行查找,找到了返回该位置的迭代器;如果不存在这个关键码,就返回
    尾迭代器。

哈希冲突解决方案

记住前三个:

线性探测
使用hash函数计算出的位置如果已经有元素占用了,则向后依次寻找,找到表尾则回到表头,直到找到
一个空位

开链
每个表格维护一个list,如果hash函数计算出的格子相同,则按顺序存在这个list中

再散列
发生冲突时使用另一种hash函数再计算一个地址,直到不冲突

二次探测
使用hash函数计算出的位置如果已经有元素占用了,按照 1 2 1^2 12 2 2 2^2 22 3 2 3^2 32…的步长依次寻找,如
果步长是随机数序列,则称之为伪随机探测

公共溢出区
一旦hash函数计算的结果相同,就放入公共溢出区

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java mybatis的SpringBoot博客论坛管理系统

java mybatis的SpringBoot博客论坛管理系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式…

Java数据结构:堆与PriorityQueue优先级队列的使用

文章目录1 什么是堆2 堆的实现思路2.1 大根堆的成员变量简介2.2 树的相关知识复习2.3 向下调整创建大根堆2.4 堆的插入2.5 堆的删除3 大根堆实现代码及测试4 PriorityQueue的使用4.1 特性简介4.2 常用方法4.3 使用PriorityQueue实现大根堆写在最后1 什么是堆 堆实质上就是对完全…

python对称加密AES的使用

python对称加密AES的使用 aes安装 pip install pycryptodome加密库引用 from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import base64完整代码 from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import base64def A…

带你玩转OpenHarmony AI-基于海思NNIE的AI能力自定义

简介相信大家从玩转OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&#xff09;AI系列专题的其他文章中&#xff0c;已经拓展了OpenHarmony AI在智慧出行、智慧办公等多场景下的AI自动检测、识别和判断的新体验&#xff0c;对于OpenHarmony平台上的AI开发有了一定认识。…

通过事件总线EventBus/AsyncEventBus进行JAVA模块解耦 (史上最全)

事件总线在 进行JAVA模块解耦 &#xff0c;价值巨大 实际开发中&#xff0c;常常 通过事件总线EventBus/AsyncEventBus进行JAVA模块解耦 &#xff0c; 比如&#xff0c;在顶级开源组件 hotkey的源码中&#xff0c; 就多次用到 EventBus/AsyncEventBus进行JAVA模块解耦 所以&am…

71、AdaNeRF: Adaptive Sampling for Real-time Rendering of Neural Radiance Fields

简介 官网&#xff1a;https://thomasneff.github.io/adanerf/ 新的双网络架构&#xff0c;它采用正交方向&#xff0c;通过学习如何最好地减少所需样本点的数量&#xff0c;将网络分为联合训练的 sample 和 shading 网络&#xff0c;训练方案在每条射线上采用固定的样本位置…

Nginx 高级篇

文章目录Nginx 高级篇一、 负载均衡1、 负载均衡概述2、 处理方式2.1 用户手动选择2.2 DNS 轮询2.3 四 / 七层负载均衡3、 七层负载均衡3.1 七层负载均衡指令3.1.1 upstream3.1.2 server3.2 实现流程3.3 负载均衡的状态3.3.1 down3.3.2 backup3.3.3 max_conns3.3.4 max_fails &…

Docker前世今生

文章目录Docker背景Docker历史docker 理念Docker能做什么虚拟机的缺点容器虚拟化技术Docker学习途径Docker背景 一款产品从开发到上线&#xff0c;从操作系统&#xff0c;到运行环境&#xff0c;再到应用配置。作为开发运维之间的协作我们需要 关心很多东西&#xff0c;这也是…

ChatGPTAPI Key申请教程

ChatGPTAPI Key申请教程 一、API Key申请使用 在浏览器打开网址&#xff1a;https://openai.com/api/ 等待网页加载完成后&#xff0c;点击右上角 LOG IN 进入登录界面 进入登录界面后&#xff0c;依次输入注册的邮箱–Continue–输入密码–Continue&#xff0c;完成登录&…

< CSS小技巧:filter滤镜妙用>

文章目录&#x1f449; 前言&#x1f449; 简述&#x1f449; 基本语法及案例》语法简述》案例&#x1f449; 拓展1. drop-shadow 更加智能的阴影效果2. 网页置灰3. 元素强调、高亮4.节省空间&#xff0c;提高网页加载速度&#x1f449; 具体案例网页参考文献往期内容 &#x1…

如何有效进行团队建设:从关注事到关注人

咱打工人都想趁着年终总结这个契机&#xff0c;拿着工作数据跟领导们提涨薪&#xff01;但是入行没多久的社畜们却没有这个底气&#xff0c;虽累但没结果&#xff08;暗指身兼数职的项目经理小白们&#xff09;&#xff0c;主要是觉得自己的工作成绩不够优秀。这几天办公室的项…

Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks

摘要 我们为元学习提出了一个算法是模型无关model−agnosticmodel-agnosticmodel−agnostic. 在某种意义上&#xff0c;其与用梯度下降训练的模型是兼容的&#xff0c;可以应用在大量不同的学习问题上。包括&#xff1a;分类、回归、和加强学习。 元学习的目标是正在学习任务…

TAZ生成实践(Intel芯片Mac Python 3.7.9)

参考文章 https://blog.csdn.net/weixin_42632778/article/details/115164518 TAZ生成 https://zhuanlan.zhihu.com/p/343576683 使用ArcGIS实现线转栅格 https://pro.arcgis.com/zh-cn/pro-app/latest/tool-reference/conversion/polyline-to-raster.htm ArcGIS Pro 折线转栅格…

第②篇 Spring IoC——容器

Spring最成功的是其提出的理念&#xff0c;而不是技术本身。 概念 Spring所依赖的两个核心理念&#xff1a; 一个是控制反转&#xff08;IoC&#xff09;。另一个是面向切面编程&#xff08;Aspect Oriented Programming&#xff0c;AOP&#xff09;。 IoC是Spring的核心&am…

JS入门到精通详解(1)

JavaScript概述(需要记)什么是javascript?是一门&#xff08;基于对象&#xff09;和&#xff08;事件驱动&#xff09;的&#xff08;脚本语言&#xff09;。js诞生于哪一年&#xff1f;哪个公司&#xff1f;谁&#xff1f;第一个名字叫什么&#xff1f;1995年 网景 布兰登 l…

【Python】type、isinstance、issubclass详解

type type方法有两种重载形式&#xff1a; type(o: object)&#xff1b;type(name: str, bases:Tuple[type, ...], dict:Mapping[str: Any], **kwds) 使用第一种重载形式的时候&#xff0c;传入一个【object】类型&#xff0c;返回一个【type】对象&#xff0c;通常与object…

解决使用element-plus时使用el-select-v2组件时,选中后无法移除focus的状态的方法。

我们可以使用element-ui-plus的el-select-v2的组件&#xff0c;实现复合搜索和下拉框的功能。 使用如下模块&#xff1a; <template><el-select-v2 v-model"value" filterable :options"options" placeholder"Please select"visibleCha…

爸妈记性变差怎么办?

记不住事的时候&#xff0c;我们总会自嘲“老了&#xff0c;脑子不好使了”。记忆力总是和年龄挂钩的&#xff0c;所以很多子女听到父母这样说&#xff0c;也不会放在心上。但有时&#xff0c;记性变差不一定因为年龄&#xff0c;还有可能是患病的前兆。当父母出现频繁忘事的情…

zerotier虚拟网络配置,局域网与外网如同局域网一样访问。

zerotier:可以搭建用于自己的虚拟网络&#xff0c;经过授权连接成功之后彼此都在同一网段&#xff0c;可以像在局域网一样互相访问。 1.创建zerotier账户 2.创建网络&#xff08;Create A Network&#xff09;并记住网络标识&#xff08;NETWORK ID&#xff09; 一、openwrt设…

中华财险进击数字化

本文来源 / 瞭望 中华联合财产保险股份有限公司&#xff08;下称中华财险&#xff09;&#xff0c;是一家 36 年的老牌国有保险公司&#xff0c;全国营业网点超过 2900 个。近年来&#xff0c;中华财险在业务高速发展的同时&#xff0c;从难啃的硬骨头下手&#xff0c;重构核心…