《STL源码剖析》(四)——序列式容器

2023-11-02

1、序列式容器

所谓序列式容器,其中的元素都可序,但未必有序,C++本身提供了一个序列式容器array,STL另外提供了vector、list、deque、stack、queue、priority-queue等序列式容器,其中stack和queue由于只是将deque改头换面,技术上归类为一种配接器(adapater)。

2、vector

2.1vecor的数据结构

数组

2.2vector和array的区别

1、vector是动态空间,随着元素增加,它的内部机制会自行扩充以容纳新的元素。
2、array是静态空间,一旦配置无法改变。
3、两者均为线性内存连续空间。

//扩大内存空间
#include<iostream>
#include<vector>
int main(){
	int a[5];
	int b[10];
	for (int i = 0; i < 5; i++)
	{b[i] = a[i]} 
	//vector
	vector<int> vec{1,2,3};
	cout<< vec.size() << endl; //size表示元素个数
	cout << vec.capacity() << endl; //capacity表示占用的空间
	vec.push_back(4);
	vec.push_back(5);
	cout << vec.size() << endl; //5
	cout<< vec.capacity() << endl; //6
}	

2.3vector内存满载情况分析

1、为了降低空间配置时的速度成本,vector的实际配置的大小可能比客户端需求量更大一些,以备将来可能使用到。
2、vector空间满载,将会进行“配置新空间/数据移动/释放旧空间”,时间成本很高,因此vecto配置新空间时,一般以旧空间的两倍来扩充。
3、空间配置:如果原来的大小为0,则配置capacity为1,否则为原来的两倍大小。

2.4vector的迭代器

1、vector维护的是一个连续性空间,普通指针即可满足要求,提供随机存取迭代器。
2、因vector满载需要进行“配置新空间/数据移动/释放旧空间”操作,因而vector迭代器会失效。

2.5vector的空间释放

1、clear()函数:清除所有的元素,但容量不改变,即size=0,capacity!=0
在这里插入图片描述
2、下面演示程序可知:当push_back和pop_back时的size大小(元素个数)改变,而capacity的大小只有在分配新空间时才改变。
3、因vector容量只增不减,所以对于释放vector占有的内存有必要了解。
方法一:与临时变量swap()
方法二:clear() + shink_to_fit(),先清除所有的元素,再将容量大小调整为元素大小即可。

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> vec{1,2,3,4,5};
	vector<int>().swap(vec); //方法1:和临时变量进行swap()
	vec.size();//0  
	vec.capacity()//0
	vec.clear();  //方法2:先清除所有元素,再将容量大小调整元素大小
	vec.shrink_to_fit(); 
}

vector的优缺点:
优点:随机存取。时间复杂度O(1)。
缺点:插入和删除的复杂度O(n),需要对元素进行移动。

3、list

3.1list的数据结构

list是一个环状双向链表,所以它只需要一个指针,就可以完整的表现整个链表。
只要刻意在环状链表的结尾加上一个空白节点,便可以满足“前闭后开”区间。在这里插入图片描述

3.2list的空间分配

list不像vector是连续存储空间,需要预留空间来节省“配置新空间/数据移动/释放旧空间”操作的成本。因而容量大小等于元素个数。
每次配置一个节点大小,当元素删除时,相应空间一并删除。

3.3list的迭代器

list不能像vector一样以普通指针作为迭代器,因为其节点不保证在存储空间中连续存在。
list是一个环状双向链表,迭代器必须具备前移、后移的能力,所以list提供双向迭代器。

3.4插入操作

在这里插入图片描述
分配一个节点,“插入…之前”

3.5list优缺点

优点:无预留空,插入和删除在常数时间内完成。
缺点:不能进行随机存取,时间复杂度为O(n).

4、deque()

4.1deque和queue的区别:

vector是单向开口的连续线性空间,deque是双向开口的连续线性空间
deque允许常数时间内对起头端的元素进行插入和删除。vector需要将头端的元素整体前移或后移,时间复杂度是O(N),效率极差。
deuqe没有所谓的容量概念,它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并连接起来。

4.2deque的中控器:

deque采用一段所谓的map(不是map容器)作为主控,这里所谓的map是一小块连续空间,其中每个元素都是指针,指向另一段(较大的)连续空间,称为缓冲区,缓冲区是deque的存储空间主体。
deque的连续空间是假象,是由一段一段的定量连续空间构成。
map其实是一个T**,也就是一个指针,所指之物是一个指针,指向型别为T的一块空间。
在这里插入图片描述

4.3deque的迭代器

deque的迭代器包含四个指针:
cur迭代器指向缓冲区的现行元素
first指向缓冲区的头部
last指向缓冲区的尾
node指向管控中心
当map指向的缓冲区容量满载时,需要对map进行扩充,扩充过程,配置更大的,拷贝原来的,释放原来的,因此deque的迭代器会失效。
deque的中控器,缓冲器,迭代器相互关系:
在这里插入图片描述
deque在尾端添加元素,引发新缓冲区的配置。在这里插入图片描述
deque在最前端添加元素,引发新缓冲区的配置。在这里插入图片描述

4.4deque的数据结构

class deque{
	iterator strat;  //指向第一个节点
	iterator finish; //指向最后一个节点
	map_pointer map; //(T **类型)指向map是一块连续空间,每个元素是一个指针,指向一个缓冲区
	size_type map_size; //map内的指针数量 
}

4.5deque如何模拟连续空间

reference operator[](size_type n)
{	return start[difference_type(m)];}
reference front()
{	return * start;}
reference back()
{	iterator tmp = finish;
	--tmp;
	return *tmp;}
size_type size()cosnt
{	return finsh-start;}
bool empty() const
{	return finish == start;}
reference operator*() const
{	return *cur;}
pointer operator->()const
{	return &(operator*());}
//两个iterator之间的距离相当于:(1)两个迭代器之间buffer的总长度+(2)迭代器1到尾部的长度+(3)x到迭代器2头的长度
difference_type operator-(const self&x) const
{
	return difference_type(buffer_size()) *(node - x.node - 1) + (cur - first) +(x.last - x.cur);
}
self& operator++()
{++cur;
if(cur == last){
	set_node(node+ 1); //如果到达尾部则跳跃至下一节点的起点
	cur = first;
	}
	return *this;
}
// 后置++ 和--同理
self& operator+=(difference_type n)
{
	difference_type offset = n+ (cur-first);
	if(offsrt >=0 && offset < buffer_size()) // 在同一个缓冲区中
	{	cur +=n;
	}
	else{  //不在同一个缓冲区中
		difference_type node_offset = offset >0? offset/buffer_size():(-offset-1)/buffer_size() - 1;
	}
	set_node(node+node_offset);
	cur = first+(offset- node_offset * buffer_size());
	return * this;
}
// +, -= [(difference_type)n]操作中使用+= 实现

4.6deque优缺点

1、可随机存取,时间复杂度为O(1)。可在头部和尾部进行插入操作,相对于vector的头插效率更高。
2、插入和删除的时间复杂度为O(N),(头插删和尾插删是O(1))若插入位置之前的个数较少,则前面元素 前移,否则后面元素后移。

5、queue和stack

5.1queue

template<class T, class Sequence=deque<T>>
class queue{
protecred:
	Sequence c; //底层容器
public:
	bool empty()const{return c.empty();}
	size_type size() const{return c.size();}
	reference front() {return c.front();}
	const_reference front() const {return c.front()};
	reference back() {return c.back();}
	const_reference back() const {return c.back();}
	void push(const value_type &x) {c.push_back(x);}
	void pop() {c.pop_front();}
}

可以用list和deque实现queue,不可以用map、set、vector实现。

5.2stack

template<class T, class Sequence=deque<T>>
class stack{
protecred:
	Sequence c; //底层容器
public:
	bool empty()const{return c.empty();}
	size_type size() const{return c.size();}
	reference top() {return c.back();}
	const_reference top() const {return c.back()};
	void push(const value_type &x) {c.push_back(x);}
	void pop() {c.pop_back();}
};

可以用list、deque和vetor实现stack,不可以用map和set。
queue和stack都不提供迭代器,因为这两种容器不允许遍历元素。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《STL源码剖析》(四)——序列式容器 的相关文章

随机推荐

  • HDU - 2100 Lovekey

    XYZ 26进制数是一个每位都是大写字母的数字 A B C X Y Z 分别依次代表一个0 25 的数字 一个 n 位的26进制数转化成是10进制的规则如下 A0A1A2A3 An 1 的每一位代表的数字为a0a1a2a3 an 1 则该X
  • 达梦数据库sql实现查询当天的数据

    1 库数据 2 sql语句 select
  • 推荐一款优秀电商开源项目

    简介 本文给大家推荐博主自己开源的电商项目newbee mall pro 在newbee mall项目的基础上搭建而来 使用 mybatis plus 作为 orm 层框架 并添加了一系列高级功能以及代码优化 特性如下 商城首页 为你推荐
  • C语言中的typeof关键字

    typeof关键字是C语言中的一种新扩展 返回变量的类型 从本质上讲 它更像是使用typedef定义一个类型名称 typeof的参数可以是两种形式 表达式或类型 1 下面是使用表达式的的例子 typeof x 0 1 这里假设x是一个函数指
  • 制作Android程序的应用图标并应用

    方案一 1 首先在网址 http romannurik github io AndroidAssetStudio icons launcher html 下载自己喜欢的图标 包含不同尺寸的图片 2 然后将下载的压缩包解压 并将里面的res文
  • 在Android里显示网页的多种方式

    在Android中显示网页主要有两种方式 一种是在Activity里面直接显示网页 另一种是调用浏览器显示网页 方式不同 使用的方法也不同 下面我们分别讲解 一 在Activity里面直接显示网页 1 在Manifest xml文件里添加I
  • android 功能模块之通讯模块五

    Android通讯录开发之获取运营商号码段 移动 联通 电信 2014年1月8日 开发记录 碎碎念 2014年的第一篇博客 原本是想写一篇随笔来开头 只因自己太懒把这件事忘记了 或者根本就不想写 我当实习生也当了接近半年了 工作上的内容和学
  • LLM总结(持续更新中)

    最新的参见LLM Summary 引言 当前LLM模型火出天际 但是做事还是需要脚踏实地 此文只是日常学习LLM 顺手整理所得 本篇博文更多侧重对话 问答类LLM上 其他方向 代码生成 这里暂不涉及 可以去看综述来了解 之前LLM模型梳理
  • 正态分布与均匀分布之间的变换

    一 任何分布都能化为 0 1 0 1 0 1 均匀分布 假设 F X a p x a F X a p x le a FX a p x a 为累积分布函数 f x f x f x 为概率密度函数 F X a a f x d x F X a i
  • stm32 IAP引导两个APP出现的问题及解决方法

    最近在做bootloader引导app时发现如果APP有操作系统时 会引导不起来 现象如下 平台 stm32H743 采用stm32cubemx配置的HAL库 测试方式 上电后运行boot loader bootloader是裸机运行 由b
  • cmake使用笔记

    vim CMakeLists txt mkdir build cd build cmake 创建 CMakeLists txt 添加内容 cmake minimum required VERSION 3 26 工程名称 project he
  • OSI、TCP/IP模型及协议

    文章目录 OSI模型 TCP IP模型 TCP协议 TCP报文 首部字段 数据字段 三次握手 三次握手时c s的状态 四次挥手 UDP协议 HTTP协议 HTTP 协议下的消息类型 Cookie Session HTTPS 常见web攻击技
  • SpringBoot配置默认Json解析工具以及空值处理方式

    SerializeConfig config new SerializeConfig 设置序列化为下划线 config propertyNamingStrategy PropertyNamingStrategy SnakeCase Stri
  • Android UI架构(十三)--App请求切换帧率(4)之SurfaceFlinger切换帧率.md

    文章目录 参考资料 简述 一 SurfaceFlinger接受帧率变化 1 1 SurfaceFlinger setDesiredActiveConfig 1 2 SurfaceFlinger repaintEverythingForHWC
  • 用C语言编写简化版银行系统:ATM取款机

    1 问题描述 用c语言编写一个简化的银行自动存款系统 适合刚接触C语言 尝试编写100多行代码的初学者作为参考 该代码编写围绕着银行ATM机器的4个业务 分别是查询 取款 存款 修改密码 其中需要两个文件 一个为DrawMoney txt文
  • 阿里巴巴开源限流系统 Sentinel 全解析

    今年下半年阿里开源了自研的限流系统 Sentinel 官方对 Sentinel 的介绍中用到了一系列高大山的名词诸如 限流 熔断降级 流量塑形 系统负载保护等 还有漂亮的形容词诸如 轻巧 专业 实时等 作为技术消费者看到这样的广告词之后禁不
  • 掌握GDB调试工具,轻松排除bug!

    一 什么是GDB gdb是GNU debugger的缩写 是编程调试工具 GDB官网 https www gnu org software gdb GDB适用的编程语言 Ada C C objective c Pascal 等 GDB的工作
  • Python-第三方库requests详解

    Requests 是用Python语言编写 基于 urllib 采用 Apache2 Licensed 开源协议的 HTTP 库 它比 urllib 更加方便 可以节约我们大量的工作 完全满足 HTTP 测试需求 Requests 的哲学是
  • C++中的namespace(using namespace)的详细理解

    在C 语言编写的程序中 变量和函数等的作用范围是有一定限制的 比如 在函数体中定义的一个临时变量就不可以在函数体外使用 为了解决变量和函数等的作用范围 在C 语言中引入了名空间的概念 并增加了关键字namespace和using 在一个名空
  • 《STL源码剖析》(四)——序列式容器

    1 序列式容器 所谓序列式容器 其中的元素都可序 但未必有序 C 本身提供了一个序列式容器array STL另外提供了vector list deque stack queue priority queue等序列式容器 其中stack和qu