当前位置: 首页 > news >正文

《STL基础之vector、list、deque》

【vector、list、deque导读】vector、list、deque这三种序列式的容器,算是比较的基础容器,也是大家在日常开发中常用到的容器,因为底层用到的数据结构比较简单,笔者就将他们三者放到一起做下对比分析,介绍下基本用法,对比下三者的性能。

     

1. vector特性和原理   

     vector是个很基础的容器,其内部也就是一段连续的内存空间,具有动态扩容的能力,支持随机访问容器中的元素,查找元素的时间复杂度是O(1),插入、删除元素(除开尾部,而且vector还有备用空间的情况)会引起内存的拷贝,存在性能问题。vector提供常用的元素操作接口有:push_back、pop_back、erase、clear、insert。还有获取vector大小的size()接口、容量的capacity()接口。

    下面给出一些示例,演示vector是如何去操作元素的?

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;class A
{
public:A() {cout << "A()" << endl;}~A(){cout << "~A()" << endl;}A(const A& other){cout << "A(const A& other)" << endl;}A& operator=(const A& other){cout << "A& operator=(const A& other)" << endl;}A(const A&& other){cout << "A(const A&& other)" << endl;}A& operator= (const A&& other){cout << "A& operator= (const A&& other)" << endl;}
};int main()
{A aa;vector<A> iv(2, aa);std::cout << "size: " << iv.size() << " capacity: " << iv.capacity() << endl;std::cout << "after push back" << std::endl;iv.push_back(aa);std::cout << "size: " << iv.size() << " capacity: " << iv.capacity() << endl;return 0;
}

运行结果如下:

图片

    iv初始化的时候,往容器中插入了两个A类对象,调用了A的拷贝构造函数两次,此时iv元素个数和容量大小都是2,随后又往iv尾部插入一个A类对象,因为iv没有多余的剩余空间,那么此时vector另外寻找了个新的空间,大小为3,并把之前的两个A类对象拷贝到新的空间中去。为啥动态扩容之后,vector的大小变成了3,而不是原来大小的两倍?很炸裂,那我们就调试最新的STL源码。

图片

    很震惊,STL做了优化和改进,动态扩容不再是两倍的扩充了,而是根据元素的实际个数来扩充,所以以前老旧的观念需要改正。

std::cout << "after pop back" << std::endl;
iv.pop_back();
iv.pop_back();
std::cout << "size: " << iv.size() << " capacity: " << iv.capacity() << endl;
 

这个时候,我们在尾部弹出两个元素,那么此时又是一种什么结果?

图片

     弹出两个元素,引起A类对象的析构,元素个数变成了1,容量的大小依然是3。

     vector的删除接口erase,有按照范围删除、也有删除指定位置的元素,这两个接口的源码如下:


iterator erase(iterator first, iterator last)
{iterator i = copy(last, finish, first);destory(i, finish);finish = finish - (last - first);return first;
}iterator erase(iterator position)
{if (position + 1 != end())copy(position + 1, finish, position);--finish;destory(finish);return position;      
}

        可以看出无论是删除指定范围的元素还是删除指定位置的元素,都会涉及到元素的拷贝或者移动赋值;以下示例程序也能验证我们的结论。

std::cout << " after erase " << std::endl;
iv.erase(iv.begin(), iv.begin() + 1);
std::cout << "size: " << iv.size() << " capacity: " << iv.capacity() << endl;

图片

    从上述运行结果可以看到,删除iv容器中首个元素,引起了后面两个元素的移动,也即第二个元素挪到第一个位置去,第三个元素挪到第二个位置去。

2、 list特性和原理

     list背后的数据结构是环状双向链表,支持元素的双向遍历查找,因此list容器在元素查找上的时间复杂度为O(n),但是插入元素、删除元素的时间复杂度始终为O(1)。list支持的元素操作有push_front、push_back、erase、pop_front、pop_back、remove、unique、merge、reverse、sort,其实这些操作,无非就是对底层的链表进行头部插入、尾部删除、翻转、排序、合并等操作。


#include <iostream>
#include <list>int main()
{list<A> li;std::cout << li.size() << endl;A a;li.push_back(a);li.push_back(a);li.insert(li.begin(), a);li.erase(li.begin());return 0;
}

  运行结果:

图片

    可以看出往list容器中push元素或者insert元素,都会引起元素的拷贝构造。

3、 deque特性和原理

    deque 是由一段一段定量连续的空间构成,一旦需要在deque的前面或者尾端增加新空间,此时只需申请一段定量的连续空间,串接在deque的头部或者尾端。deque的整体架构图如下:

图片

       map并不是键值对map,而是一个指针数组,里面存储的是一个个指针,里面每个指针指向一段段连续的内存空间,这些分段的内存分别用来存储数据。虽然内存是分段的,但是给外部的表象是连续的内存空间,原因在于deque的迭代器设计的很巧妙。


template<class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator
{typedef T** map_pointer;  //指向管控中心maptypedef __deque_iterator self;T* cur;  //指向缓冲区当前的元素T* first; //指向缓冲区一个元素T* last; //指向缓冲区最后一个元素map_pointer node; //管控中心的节点
}

      假设我们在遍历元素的时候,走到了第二个缓冲区的末尾节点,此时,应该如何跳转到下一个缓冲区,且看deque的源码。


void set_node(map_pointer new_node)
{node = new_node;//下一个节点的首位元素便是firstfirst = *new_node;last = first + difference_type(buffer_size());
}self& operator++()
{++cur;if (cur == last){//跳转到下一个节点set_node(node + 1);cur = first;}return *this;
}

     好,再验证下deque插入元素,是否会涉及到插入对象的拷贝。


A a;
deque<A> idque(2, a);
idque.push_back(a);
idque.push_front(a);
idque.insert(idque.begin(), a);
idque.insert(idque.end(), a);

图片

      在头部、尾部插入元素,只会拷贝当前的对象,并不会涉及到其它对象的拷贝或者移动。那如果在容器的中间端插入对象呢?


cout << "after insert" << endl;
idque.insert(idque.begin() + 2, a);

图片

    可以清晰看到,在中间部位插入对象,还是会影响到其它元素的移动,现在新版的STL倒是做了优化和改进,使用移动构造或者移动赋值的方式去搬移对象,而不是单纯地拷贝构造或赋值。

4、 性能比对

int main()
{// 获取当前时间作为示例auto start = std::chrono::system_clock::now();A a;deque<A> idque;time_t t1 = time(NULL);for (int i = 0; i < 100 * 10000; ++i){idque.push_front(a);}// 计算差值auto end = std::chrono::system_clock::now();auto duration = end - start;cout << "deque: " << duration.count() << endl;list<A> li;start = std::chrono::system_clock::now();for (int i = 0; i < 100 * 10000; ++i){li.push_back(a);}end = std::chrono::system_clock::now();duration = end - start;cout << "list: " << duration.count() << endl;vector<A> iv;start = std::chrono::system_clock::now();for (int i = 0; i < 100 * 10000; ++i){iv.push_back(a);}end = std::chrono::system_clock::now();duration = end - start;cout << "vector: " << duration.count() << endl;return 0;
}

图片

相关文章:

《STL基础之vector、list、deque》

【vector、list、deque导读】vector、list、deque这三种序列式的容器&#xff0c;算是比较的基础容器&#xff0c;也是大家在日常开发中常用到的容器&#xff0c;因为底层用到的数据结构比较简单&#xff0c;笔者就将他们三者放到一起做下对比分析&#xff0c;介绍下基本用法&a…...

LockSupport概述、阻塞方法park、唤醒方法unpark(thread)、解决的痛点、带来的面试题

目录 ①. 什么是LockSupport? ②. 阻塞方法 ③. 唤醒方法(注意这个permit最多只能为1) ④. LockSupport它的解决的痛点 ⑤. LockSupport 面试题目 ①. 什么是LockSupport? ①. 通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作 ②. LockSupport是一个线程阻塞…...

Android开发基础知识

1 什么是Android&#xff1f; Android&#xff08;读音&#xff1a;英&#xff1a;[ndrɔɪd]&#xff0c;美&#xff1a;[ˈnˌdrɔɪd]&#xff09;&#xff0c;常见的非官方中文名称为安卓&#xff0c;是一个基于Linux内核的开放源代码移动操作系统&#xff0c;由Google成立…...

C++ Lambda 表达式的本质及原理分析

目录 1.引言 2.Lambda 的本质 3.Lambda 的捕获机制的本质 4.捕获方式的实现与底层原理 5.默认捕获的实现原理 6.捕获 this 的机制 7.捕获的限制与注意事项 8.总结 1.引言 C 中的 Lambda 表达式是一种匿名函数&#xff0c;最早在 C11 引入&#xff0c;用于简化函数对象的…...

《多线程基础之条件变量》

【条件变量导读】条件变量是多线程中比较灵活而且容易出错的线程同步手段&#xff0c;比如&#xff1a;虚假唤醒、为啥条件变量要和互斥锁结合使用&#xff1f;windows和linux双平台下&#xff0c;初始化、等待条件变量的api一样吗&#xff1f; 本文将分别为您介绍条件变量在w…...

21款炫酷烟花合集

系列专栏 《Python趣味编程》《C/C趣味编程》《HTML趣味编程》《Java趣味编程》 写在前面 Python、C/C、HTML、Java等4种语言实现18款炫酷烟花的代码。 Python Python烟花① 完整代码&#xff1a;Python动漫烟花&#xff08;完整代码&#xff09; ​ Python烟花② 完整…...

智能风控 数据分析 groupby、apply、reset_index组合拳

目录 groupby——分组 本例 apply——对每个分组应用一个函数 等价用法 reset_index——重置索引 使用前​编辑 注意事项 groupby必须配合聚合函数、 关于agglist 一些groupby试验 1. groupby对象之后。sum&#xff08;一个列名&#xff09; 2. groupby对象…...

Python网络自动化运维---用户交互模块

文章目录 目录 文章目录 前言 实验环境准备 一.input函数 代码分段解析 二.getpass模块 前言 在前面的SSH模块章节中&#xff0c;我们都是将提供SSH服务的设备的账户/密码直接写入到python代码中&#xff0c;这样很容易导致账户/密码泄露&#xff0c;而使用Python中的用户交…...

【JVM】调优

目的&#xff1a; 减少minor gc、full gc的次数&#xff0c;也就是减少STW的时间&#xff0c;因为java虚拟机在做后台垃圾收集线程的时候&#xff0c;会停掉其他线程&#xff0c;专门做垃圾收集&#xff0c;这样会影响网站的性能&#xff0c;以及用户的体验。 调优位置&#x…...

软件测试 —— jmeter(2)

软件测试 —— jmeter&#xff08;2&#xff09; HTTP默认请求头&#xff08;元件&#xff09;元件作用域和取样器作用域HTTP Cookie管理器同步定时器jmeter插件梯度压测线程组&#xff08;Stepping Thread Group&#xff09;参数解析总结 Response Times over TimeActive Thre…...

为什么LabVIEW适合软硬件结合的项目?

LabVIEW是一种基于图形化编程的开发平台&#xff0c;广泛应用于软硬件结合的项目中。其强大的硬件接口支持、实时数据采集能力、并行处理能力和直观的用户界面&#xff0c;使得它成为工业控制、仪器仪表、自动化测试等领域中软硬件系统集成的理想选择。LabVIEW的设计哲学强调模…...

【机器学习】自定义数据集 使用tensorflow框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测

一、使用tensorflow框架实现逻辑回归 1. 数据部分&#xff1a; 首先自定义了一个简单的数据集&#xff0c;特征 X 是 100 个随机样本&#xff0c;每个样本一个特征&#xff0c;目标值 y 基于线性关系并添加了噪声。tensorflow框架不需要numpy 数组转换为相应的张量&#xff0…...

.NET Core缓存

目录 缓存的概念 客户端响应缓存 cache-control 服务器端响应缓存 内存缓存&#xff08;In-memory cache&#xff09; 用法 GetOrCreateAsync 缓存过期时间策略 缓存的过期时间 解决方法&#xff1a; 两种过期时间策略&#xff1a; 绝对过期时间 滑动过期时间 两…...

GA-CNN-LSTM-Attention、CNN-LSTM-Attention、GA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比

GA-CNN-LSTM-Attention、CNN-LSTM-Attention、GA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比 目录 GA-CNN-LSTM-Attention、CNN-LSTM-Attention、GA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于GA-CNN-LST…...

git Bash通过SSH key 登录github的详细步骤

1 问题 通过在windows 终端中的通过git登录github 不再是通过密码登录了&#xff0c;需要本地生成一个密钥&#xff0c;配置到gihub中才能使用 2 步骤 &#xff08;1&#xff09;首先配置用户名和邮箱 git config --global user.name "用户名"git config --global…...

《企业应用架构模式》笔记

领域逻辑 表模块和数据集一起工作-> 先查询出一个记录集&#xff0c;再根据数据集生成一个&#xff08;如合同&#xff09;对象&#xff0c;然后调用合同对象的方法。 这看起来很想service查询出一个对象&#xff0c;但调用的是对象的方法&#xff0c;这看起来像是充血模型…...

深入理解 C 语言函数指针的高级用法:(void (*) (void *)) _IO_funlockfile

深入理解 C 语言函数指针的高级用法 函数指针是 C 语言中极具威力的特性&#xff0c;广泛用于实现回调、动态函数调用以及灵活的程序设计。然而&#xff0c;复杂的函数指针声明常常让即使是有经验的开发者也感到困惑。本文将从函数指针的基本概念出发&#xff0c;逐步解析复杂…...

【JavaSE】图书管理系统

前言&#xff1a;为了巩固之前学习的java知识点&#xff0c;我们用之前学习的java知识点&#xff08;方法&#xff0c;数组&#xff0c;类和对象&#xff0c;封装&#xff0c;继承&#xff0c;多态&#xff0c;抽象类&#xff0c;接口&#xff09;来实现一个简单的图书管理系统…...

【C++数论】880. 索引处的解码字符串|2010

本文涉及知识点 数论&#xff1a;质数、最大公约数、菲蜀定理 LeetCode880. 索引处的解码字符串 给定一个编码字符串 s 。请你找出 解码字符串 并将其写入磁带。解码时&#xff0c;从编码字符串中 每次读取一个字符 &#xff0c;并采取以下步骤&#xff1a; 如果所读的字符是…...

C++/stack_queue

目录 1.stack 1.1stack的介绍 1.2stack的使用 练习题&#xff1a; 1.3stack的模拟实现 2.queue的介绍和使用 2.1queue的介绍 2.2queue的使用 2.3queue的模拟实现 3.priority_queue的介绍和使用 3.1priority_queue的介绍 3.2priority_queue的使用 欢迎 1.stack 1.1stack…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...