C++容器详解

news2024/11/17 12:32:01

什么是容器

首先,我们必须理解一下什么是容器,在C++ 中容器被定义为:在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对像的指针,这种对象类型就叫做容器。很简单,容器就是保存其它对象的对 象,当然这是一个朴素的理解,这种“对象”还包含了一系列处理“其它对象”的方法,因为这些方法在程序的设计上会经常被用到,所以容器也体现了一个好处, 就是“容器类是一种对特定代码重用问题的良好的解决方案”。

容器还有另一个特点是容器可以自行扩展。在解决问题时我们常常不知道我们需要存储多少个对象,也就是说我们不知道应该创建多大的内存空间来保存我们的对象。 显然,数组在这一方面也力不从心。容器的优势就在这里,它不需要你预先告诉它你要存储多少对象,只要你创建一个容器对象,并合理的调用它所提供的方法,所 有的处理细节将由容器来自身完成。它可以为你申请内存或释放内存,并且用最优的算法来执行您的命令。容器是随着面向对象语言的诞生而提出的,容器类在面向对象语言中特别重要,甚至它被认为是早期面向对象语言的基础。在现在几乎所有的面向对象的语言中也都伴随着一个容器集,在C++ 中,就是标准模板库(STL )。和其它语言不一样,C++ 中处理容器是采用基于模板的方式。标准C++ 库中的容器提供了多种数据结构,这些数据结构可以与标准算法一起很好的工作,这为我们的软件开发提供了良好的支持!

通用容器的分类

我们通常将STL标准库中的通用容器分为以下三大类

1.顺序性容器

顺序性容器 是 一种各元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。这个位置和 元素本身无关,而和操作的时间和地点有关,顺序性容器不会根据元素的特点排序而是直接保存了元素操作时的逻辑顺序。比如我们一次性对一个顺序性容器追加三 个元素,这三个元素在容器中的相对位置和追加时的逻辑次序是一致的。

接下来我们依次介绍STL标准库中的容器

1.vector容器

当需要使用数组的情况下,可以考虑使用vector

 1.特点:

 (1) 一个动态分配的数组(当数组空间内存不足时,都会执行: 分配新空间-复制元素-释放原空间);

 (2) 当删除元素时,不会释放限制的空间,所以向量容器的容量(capacity)大于向量容器的大小(size);

 (3) 对于删除或插入操作,执行效率不高,越靠后插入或删除执行效率越高;

 (4) 高效的随机访问的容器。

 2.创建vecotr对象:

 (1) vector<int> v1;

 (2) vector<int> v2(10);  

 3.基本操作:

 v.capacity();  //容器容量

 v.size();      //容器大小

 v.at(int idx); //用法和[]运算符相同

 v.push_back(); //尾部插入

 v.pop_back();  //尾部删除

 v.front();     //获取头部元素

 v.back();      //获取尾部元素

 v.begin();     //头元素的迭代器

 v.end();       //尾部元素的迭代器

 v.insert(pos,elem); //pos是vector的插入元素的位置

 v.insert(pos, n, elem) //在位置pos上插入n个元素elem

 v.insert(pos, begin, end);

 v.erase(pos);   //移除pos位置上的元素,返回下一个数据的位置

 v.erase(begin, end); //移除[begin, end)区间的数据,返回下一个元素的位置

 reverse(pos1, pos2); //将vector中的pos1~pos2的元素逆序存储
2.queue容器

1.特点:

(1) deque(double-ended queue 双端队列);

(2) 具有分段数组、索引数组, 分段数组是存储数据的,索引数组是存储每段数组的首地址;

(3) 向两端插入元素效率较高!

    (若向两端插入元素,如果两端的分段数组未满,既可插入;如果两端的分段数组已满,

    则创建新的分段函数,并把分段数组的首地址存储到deque容器中即可)。

    中间插入元素效率较低!

 

2. 创建deque对象

(1) deque<int> d1;

(2) deque<int> d2(10);

 

3. 基本操作:

(1) 元素访问:

d[i];

d.at[i];

d.front();

d.back();

d.begin();

d.end();

(2) 添加元素:

d.push_back();

d.push_front();

d.insert(pos,elem); //pos是vector的插入元素的位置

d.insert(pos, n, elem) //在位置pos上插入n个元素elem

d.insert(pos, begin, end);

(3) 删除元素:

d.pop_back();

d.pop_front();

d.erase(pos);   //移除pos位置上的元素,返回下一个数据的位置

d.erase(begin, end); //移除[begin, end)区间的数据,返回下一个元素的位置
3.list容器

1. 特点:

(1) 双向链表

 

2.创建对象:

list<int> L1;

list<int> L2(10);

 

3.基本操作:

(1) 元素访问:

lt.front();

lt.back();

lt.begin();

lt.end();

 

(2) 添加元素:

lt.push_back();

lt.push_front();

lt.insert(pos, elem);

lt.insert(pos, n , elem);

lt.insert(pos, begin, end);

lt.pop_back();

lt.pop_front();

lt.erase(begin, end);

lt.erase(elem);

(3)sort()函数、merge()函数、splice()函数:

sort()函数就是对list中的元素进行排序;

merge()函数的功能是:将两个容器合并,合并成功后会按从小到大的顺序排列;

比如:lt1.merge(lt2); lt1容器中的元素全都合并到容器lt2中。

splice()函数的功能是:可以指定合并位置,但是不能自动排序!
c++11中还加入了forward_list

这个其实就是一个单向链表,引入这个容器的目的就是为了提供时间复杂度为O(1)的插入和删除,越靠近尾部,插入和删除的效率越高

 

2.关联性容器

关联式容器 和 顺序性容器不一样,关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置 入容器时的逻辑顺序。但是关联式容器提供了另一种根据元素特点排序的功能,这样迭代器就能根据元素的特点“顺序地”获取元素。

关联式容器另一个显著的特点是它是以键值的方式来保存数据,就是说它能把关键字和值关联起来保存,而顺序性容器只能保存一种(可以认为它只保存关键字,也可以认为它只保存值)。这在下面具体的容器类中可以说明这一点。

关联式容器的特点

(1) 关联式容器都是有序的,升序排列,自动排序;

(2) 实现的是一个平衡二叉树,每个元素都有一个父节点和两个子节点,

左子树的所有元素都比自己小,右子树的所有元素都比自己大;

1.set/multiset

1. 特点:

构造set集合的主要目的是为了快速检索,去重与排序

(1) set存储的是一组无重复的元素,而multiset允许存储有重复的元素;

(2) 如果要修改某一个元素值,必须先删除原有的元素,再插入新的元素。

2.创建对象:

set<T> s;

set<T, op(比较结构体)> s;     //op为排序规则,默认规则是less<T>(升序排列),或者是greater<T>(降序规则)。

函数对象:

class Sum

{

public:

    int operator()(int a, int b){return a+b;}

};

Sum sum;  //利用了()运算符的重载

3. 基本操作:

s.size();      //元素的数目

s.max_size();  //可容纳的最大元素的数量

s.empty();     //判断容器是否为空

s.find(elem);  //返回值是迭代器类型

s.count(elem); //elem的个数,要么是1,要么是0,multiset可以大于一

s.begin();

s.end();

s.rbegin();

s.rend();

s.insert(elem);

s.insert(pos, elem);

s.insert(begin, end);

s.erase(pos);

s.erase(begin,end);

s.erase(elem);

s.clear();//清除a中所有元素;

pair类模板
1. 主要作用是将两个数据组成一个数据,用来表示一个二元组或一个元素对,

两个数据可以是同一个类型也可以是不同的类型。

当需要将两个元素组合在一起时,可以选择构造pair对象,

set的insert返回值为一个pair<set<int>::iterator,bool>。bool标志着插入是否成功,而iterator代表插入的位置,若该键值已经在set中,则iterator表示已存在的该键值在set中的位置。
如:set<int> a;

           a.insert(1);

           a.insert(2);

           a.insert(2);//重复的元素不会被插入;

注意一下:make_pair()函数内调用的仍然是pair构造函数
 

set中的erase()操作是不进行任何的错误检查的,比如定位器的是否合法等等,所以用的时候自己一定要注意。

创建pair对象:

pair<int, float> p1;   //调用构造函数来创建pair对象

make_pair(1,1.2);      //调用make_pair()函数来创建pair对象

pair对象的使用:

pair<int, float> p1(1, 1.2);

cout<< p1.first << endl;

cout<< p1.second << endl;

顺序遍历:
set<int> a;

set<int>::iterator it=a.begin();

for(;it!=a.end();it++)

    cout<<*it<<endl;

反序遍历:
set<int> a;

set<int>::reverse_iterator rit=a.rbegin();

for(;rit!=a.rend();rit++)

cout<<*rit<<endl;

find(key_value);//如果找到查找的键值,则返回该键值的迭代器位置,否则返回集合最后一个元素后一个位置的迭代器,即end();
如:int b[]={1,2,3,4,5};     set<int> a(b,b+5);

        set<int>::iterator it;

        it=a.find(3);

        if(it!=a.end())  cout<<*it<<endl;

        else cout<<“该元素不存在”<<endl;

        it=a.find(10);

        if(it!=a.end())  cout<<*it<<endl;

        else cout<<“该元素不存在”<<endl;

定义比较函数
     set容器在判定已有元素a和新插入元素b是否相等时,是这么做的:
(1)将a作为左操作数,b作为右操作数,调用比较函数,并返回比较值 ;

(2)将b作为左操作数,a作为右操作数,再调用一次比较函数,并返回比较值。

     也就是说,假设有比较函数f(x,y),要对已有元素a和新插入元素b进行比较时,会先进行f(a,b)操作,再进行f(b,a)操作,然后返回两个bool值。

如果1、2两步的返回值都是false,则认为a、b是相等的,则b不会被插入set容器中;

如果1返回true而2返回false,那么认为b要排在a的后面,反之则b要排在a的前面;

如果1、2两步的返回值都是true,则可能发生未知行为。

(1)自定义比较结构体;

首先,定义比较结构体

struct myComp

{

     bool operator() (const 类型 &a, const 类型 &b)//重载“()”操作符

          {

                 ……

                return  ……;

          }

};

然后,定义set:

set<类型,myComp> s;
2.map/multimap

去重类问题
可以打乱重新排列的问题
有清晰的一对一关系的问题
1. 特点:

(1) map为单重映射、multimap为多重映射;

(2) 主要区别是map存储的是无重复键值的元素对,而multimap允许相同的键值重复出现,既一个键值可以对应多个值。

(3) map内部自建了一颗红黑二叉树,可以对数据进行自动排序,所以map里的数据都是有序的,这也是我们通过map简化代码的原因。

(4)自动建立key-value的对应关系,key和value可以是你需要的任何类型。

(5) key和value一一对应的关系可以去重。

2. 创建对象:

map<T1,T2> m;

map<T1,T2, op> m;  //op为排序规则,默认规则是less<T>

3. 基本操作:

m.at(key);

m[key];

m.count(key);

m.max_size(); //求算容器最大存储量

m.size();  //容器的大小

m.begin();

m.end();

m.insert(elem);

m.insert(pos, elem);

m.insert(begin, end);

注意一下:该容器存储的是键值对,所以插入函数与其他容器稍有不同
(1) 使用pair<>构造键值对对象

map<int, float> m;

m.insert(pair<int, float>(10,2.3));

(2)使用make_pair()函数构建键值对对象

map<int,float> m;

m.insert(make_pair(10,2.3));

(2) 使用value_type标志

map<int, float> m;

m.insert(map<int,float>::value_type(10,2.3));

m[key] = value;   //m只能是map容器,不适用于multimap

m.erase(pos);

m.erase(begin,end);

m.erase(key);
 

3.容器适配器

容器适配器 是一个比较抽象的概念, C++的 解释是:适配器是使一事物的行为类似于另一事物的行为的一种机制。容器适配器是让一种已存在的容器类型采用另一种不同的抽象类型的工作方式来实现的一种机 制。其实仅是发生了接口转换。那么你可以把它理解为容器的容器,它实质还是一个容器,只是他不依赖于具体的标准容器类型,可以理解是容器的模版。或者把它 理解为容器的接口,而适配器具体采用哪种容器类型去实现,在定义适配器的时候可以由你决定。

STL 中包含三种适配器:栈stack 、队列queue 和优先级priority_queue 。

适配器是容器的接口,它本身不能直接保存元素,它保存元素的机制是调用另一种顺序容器去实现,即可以把适配器看作“它保存一个容器,这个容器再保存所有元素”。

STL 中提供的三种适配器可以由某一种顺序容器去实现。默认下stack 和queue 基于deque 容器实现,priority_queue 则基于vector 容器实现。当然在创建一个适配器时也可以指定具体的实现容器,创建适配器时在第二个参数上指定具体的顺序容器可以覆盖适配器的默认实现。

由于适配器的特点,一个适配器不是可以由任一个顺序容器都可以实现的。

栈stack 的特点是后进先出,所以它关联的基本容器可以是任意一种顺序容器,因为这些容器类型结构都可以提供栈的操作有求,它们都提供了push_back 、pop_back 和back 操作;

队列queue 的特点是先进先出,适配器要求其关联的基础容器必须提供pop_front 操作,因此其不能建立在vector 容器上;

优先级队列priority_queue 适配器要求提供随机访问功能,因此不能建立在list 容器上。

3.各类容器及其特点

标准容器类

特点

顺序性容器

vector

从后面快速的插入与删除,直接访问任何元素

deque

从前面或后面快速的插入与删除,直接访问任何元素

list

双链表,从任何地方快速插入与删除

关联容器

set

快速查找,不允许重复值

multiset

快速查找,允许重复值

map

一对多映射,基于关键字快速查找,不允许重复值

multimap

一对多映射,基于关键字快速查找,允许重复值

容器适配器

stack

后进先出

queue

先进先出

priority_queue

最高优先级元素总是第一个出列

 

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

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

相关文章

Flutter控件之文本Text封装

Flutter控件之基类Widget封装 上篇文章&#xff0c;我们简单针对Widget做了一个基类封装&#xff0c;拓展出了很多常见又易用的属性&#xff0c;比如宽高&#xff0c;内外边距等等&#xff0c;很方便的为接下来的各个基础组件的封装&#xff0c;提供极大的便利&#xff0c;在上…

虚拟机启动时出现“已启用侧通道缓解”的解决方法

系列文章目录 Hypervisor launch failed centos7配置ssh免密登陆完成&#xff0c;进行ssh登陆时出现”代理承认未能使用密钥签名“ 解决pip更新的代码 文章目录 系列文章目录 一、问题描述 二、启用了侧通道缓解的虚拟机可能会出现性能下降 &#xff08;79832&#xff0…

Linux系统vim查看文件中文乱码

Linux系统查看文件-cat中文正常显示 vim中文乱码 1、背景2、环境3、目的4、原因5、操作步骤5.1、修改vim编码配置 6、验证 1、背景 服务器部署业务过程中查看文件内容&#xff0c;使用cat 命令查看中文正常显示&#xff0c;使用vim命令查看显示中文乱码 cat 查看 vim 查看 …

陶哲轩宣布主持白宫生成式AI工作组,李飞飞、Hassabis发表演讲

来源 | 新智源 ID | AI-era 【导读】最近&#xff0c;「数学天才」陶哲轩表示&#xff0c;自己将领导白宫生成式人工智能工作组&#xff0c;就当前AI评估并收集意见。在陶哲轩看来&#xff0c;加入工作流的ChatGPT在数学专业领域中&#xff0c;并没有太多增值。 近来&#xf…

Redis主从复制、哨兵、cluster集群原理+实验

Redis 主从复制 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。 默认情况下&#xff0c;每台Redis服务…

Fluent局部坐标系(曲线坐标系)

1 概述 在某些模型中&#xff0c;利用局部坐标系可极大的方便模型设置&#xff0c;例如对弯曲的多孔板设置多孔介质属性、设置各向异性的材料属性等。 2 创建坐标系 通过树状菜单中“curvilinear coordinate system”可创建曲线型局部坐标系。 右键点击“新建”&#xff0c;在如…

Linux 安装redis

一、概述 官网&#xff1a;https://redis.io/ Redis 是完全开源免费的&#xff0c;遵守BSD协议&#xff0c;是一个高性能的key-value数据库。 Redis 与其他 key - value 缓存产品有以下三个特点&#xff1a; Redis支持数据的持久化&#xff0c;可以将内存中的数据保持在磁盘…

基于静态和动态特征融合的语音情感识别层次网络

题目Hierarchical Network based on the Fusion of Static and Dynamic Features for Speech Emotion Recognition时间2021年期刊\会议ICASSP 基于静态和动态特征融合的语音情感识别层次网络 摘要&#xff1a;许多关于自动语音情感识别&#xff08;SER&#xff09;的研究都致…

【集群划分】基于kmeans的电压调节的集群划分【IEEE33节点】

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

HTTP/HTTPS协议详解

目录 一. HTTP详解 ✅1.1 概念 ✅1.2 HTTP的协议格式 1.2.1 HTTP请求体格式&#xff1a; 1.2.2 HTTP响应体格式&#xff1a; ✅1.3 HTTP请求方法 ✅1.4 认识请求报头 ✅1.5 HTTP请求过程 ✅1.6 认识状态码 二. HTTPS详解 ✅2.1 HTTPS简介 ✅2.2 HTTPS加密过程 TCP/UDP是位于传…

d3d(Direct X)中的com技术详解

本文不会对Com进行非常详细的分析 因为这个技术分析起来难度还是非常大的 要想真正弄懂还是非常困难的 我只会针对d3d中使用到的com技术和comptr技术进行说明 所以看完本文后 可以熟练使用d3d中使用到的相应技术 comptr类似于c11中的智能指针,用于管理com对象的生命周期,所以我…

深度学习基础篇之卷积神经网络(CNN)

一、CNN的基本结构 首先我们来看CNN的解百纳结构&#xff0c;一个常见的图像识别CNN模型如下图&#xff1a; 从图中可以看出最左边的图像就是模型的输入层&#xff0c;在计算机中就是若干个矩阵&#xff0c;这点与DNN类似。 接着是卷积层&#xff08;Convolution Layer&…

rtmp协议

目录 1 rtmp格式 2 header 3 chunk data 1 rtmp格式 Real Time Messaging Protocol&#xff08;实时消息传送协议协议)是Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输开发的私有协议。 在RTMP协议中信令和媒体数据都称之为Message&#xff0c;包含Mess…

Intellij IDEA 提示 Thrift Support 支持不兼容

最近升级 Intellij IDEA 后老提示 Thrift Support 不兼容。 后来看了下这个插件已经不少时间没有更新了&#xff0c;也一直不知道这个插件是干什么 用的&#xff0c; 后来看了下&#xff0c;这个插件是&#xff1a; Thrift是一种接口描述语言和二进制通讯协议&#xff0c;它被…

【031】基于Vue的学生宿舍管理系统课设(含源码、数据库、运行教程

前排提示&#xff1a;项目源码已放在文末 基于VueSpringbootmysql员工考勤管理系统(多角色登录、请假、打卡) 开发环境&#xff1a;SpringbootMysqlVueNodejsMavenJDK1.8&#xff0b;redis 技术栈&#xff1a;spring-boot、mysql、mybatis-plus 数据库&#xff1a; 源码、…

Centos7单机部署Flink13.6及测试FinkCDC同步MySQL

一、背景 公司CDH6.3.2里面的版本是Flink1.12.0。而因为FlinkCDC2.0.0只支持Flink1.13.0以后&#xff0c;版本不匹配&#xff0c;所以只能升级版本。但是升级版本是个大工程&#xff0c;要编译、要parcel制作工具&#xff0c;而且是生产环境的升级&#xff0c;没办法因为要测试…

初识Spring MVC框架,Spring MVC工作原理

Java EE三层架构 在Java EE开发中&#xff0c;系统经典的三层架构包括表现层、业务层和持久层。三层架构中&#xff0c;每一层各司其职&#xff0c;表现层(Web层&#xff09;负责接收客户端请求&#xff0c;并向客户端响应结果;业务层( Service层&#xff09;负责业务逻辑处理…

3 手工推导Neural Networ

线性模型假设的问题 如上图&#xff0c;对非线性类边界的数据进行分类 一个解决方案是将数据映射到更高维的空间&#xff0c;就变成线性可分的了。 ϕ \phi ϕ 是一个映射函数&#xff0c;将x从一个低维空间映射到高维空间。 ϕ \phi ϕ 可不可以是一个线性函数&#xff1f; …

RK3568平台开发系列讲解(驱动基础篇)GPIO使用以及gpio-leds驱动讲解

🚀返回专栏总目录 文章目录 一、GPIO 介绍二、RK3568 GPIO 状况三、GPIO 引脚计算四、ITX-3568JQ LED4.1 LED 原理图4.2 LED 设备树4.3 LED 使用五、gpio-leds驱动5.1 介绍5.2 数据结构5.3 驱动分析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 GPIO使用…

[Hadoop]Apache Hadoop、HDFS

目录 大数据导论与Linux基础 Apache Hadopp概述 Hadoop介绍 Hadoop现状 Hadoop特性优点 Hadopp架构变迁 Apache Hadopp集群搭建 Hadopp集群简介 Hadoop集群模式安装 Hadoop集群启停命令、Web UI HDFS分布式文件系统基础 分布式存储系统的核心属性及功能含义 HDFS简…