C++ STL容器

STL 容器

C++STL容器是C++的利器之一,在很多问题中使用非常方便。

顺序容器

vector向量容器
  • 类模板实现,使用时需要实例化,底层数据结构:动态开辟的数组,每次以原来空间大小的2倍方式扩容。
接口名称使用用法功能描述时间复杂度
push_backpush_back(val)末尾添加元素0(1)
insertinsert(it,val)迭代器位置添加元素0(n)
pop_backpop_back()末尾删除元素0(1)
eraseerase(it)删除迭代器位置元素0(n)
operator []vec[val]下标访问元素0(1)
findfind(val)查找元素,返回迭代器0(n)
sizesize()返回容器中元素个数
emptyempty()判断容器空
reservereserve(val)为数组预留空间,可以解决数组扩容效率低下问题
resizeresize(num)为数组扩容,不经开辟空间,而且填充元素
swapswap(vec1)交换两个容器的内容
遍历容器
int main()
{
	vector<int> vec;
	for (int i = 0; i < 10; ++i)
	{
		vec.push_back(rand() % 100);
	}
    
    // 下标访问
    for(int i = 0;i<vec.size();++i)
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    
	/*
	迭代器遍历容器
	迭代器是容器的内置类型,因此需要加作用域
	begin方法返回容器的起始位置
	*/ 
	vector<int>::iterator it = vec.begin();
	for (; it != vec.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;
    
	// 使用auto自动匹配类型
	auto it1 = vec.begin();
	for (; it1 != vec.end(); ++it1)
	{
		cout << *it1 << " ";
	}
	cout << endl;
    
	// 使用foreach,底层也是迭代器
	for (int val : vec)
	{
		cout << val << " ";
	}
	cout << endl;
	return 0;
}
容器迭代器失效问题
  • 使用迭代器连续删除和插入容器时,需要注意迭代器失效的问题。
int main()
{
	vector<int> vec;
	for (int i = 0; i < 10; ++i)
	{
		vec.push_back(rand() % 100);
	}
    /*
    连续删除元素,需要更新迭代器
    */
	auto it = vec.begin();
	for (; it != vec.end();)
	{
		if (*it % 2 == 0)
		{
			it = vec.erase(it); // 更新迭代器
		}
		else
		{
			++it;
		}
	}
	return 0;
}
deque双端队列容器
  • 类模板实现的,使用时需要实例化,底层是动态开辟的二维数组,deque的底层二维数组大小的方式
#define MAX_SIZE 2 // 一维数组
#define QUE_SZIE 4096 / sizeof(T) // 二维数组的大小
  • 由此可知,deque的二维的大小是固定大小开辟的,内存扩容时,扩容第一维的数组空间,每次按照2倍方式扩容,然后将内容调整后,使得上下预留空间。
函数接口使用方法功能描述时间复杂度
push_backpush_back(val)从队列尾部插入元素o(1)
push_frontpush_front(val)从队列首部添加元素o(1)
insertinsert(it,val)在迭代器的位置添加元素o(n)
pop_backpop_back()从队列尾部删除元素o(1)
pop_frontpop_front()从队列头部删除元素o(1)
eraseerase(it)在迭代器的位置删除元素o(n)

在这里插入图片描述

list双向循环链表
  • 底层是带头节点的双向循环链表。
函数接口使用方法功能描述时间复杂度
push_backpush_back(val)从队列尾部插入元素o(1)
push_frontpush_front(val)从队列首部添加元素o(1)
insertinsert(it,val)在迭代器的位置添加元素o(1)
pop_backpop_back()从队列尾部删除元素o(1)
pop_frontpop_front()从队列头部删除元素o(1)
eraseerase(it)在迭代器的位置删除元素o(1)
  • list虽然删除插入本身是0(1),但是每次都需要从链表头开始遍历,时间复杂度是0(n)。
splice成员方式
  • 因为链表的每个节点都是单独开辟内存的,因此链表的移动或者将两个链表之间移动数据的时候,直接将节点摘下来,然后链接到新的位置,效率比较高。
splice使用方式
int main()
{
	list<int> lst1;
	list<int> lst2;
	for (int i = 0; i < 10; ++i)
	{
		lst1.push_back(rand() % 100);
	}
	for (int i = 0; i < 10; ++i)
	{
		lst2.push_back(rand() % 100);
	}
	for (int val : lst1)
	{
		cout << val << " ";
	}
	cout << endl;
	for (int val : lst2)
	{
		cout << val << " ";
	}
	cout << endl;

	// 将lst1的首元素移动到lst1的末尾,不涉及内存开辟,只是将指针域改变。
	lst1.splice(lst1.end(), lst1, lst1.begin());
	for (int val : lst1)
	{
		cout << val << " ";
	}
	cout << endl;
	
	// 将lst1的首元素移动到lst2的末尾,
	lst2.splice(lst2.end(), lst1, lst1.begin());
	for (int val : lst2)
	{
		cout << val << " ";
	}
	cout << endl;

	// 将lst1添加到lst2的末尾。
	lst2.splice(lst2.end(),lst1,lst1.begin(), lst1.end());
	for (int val : lst2)
	{
		cout << val << " ";
	}
	cout << endl;
	return 0;
}
  • 由代码可以看出,splice函数也是使用迭代器进行的,第一个参数是调用splice函数的list的迭代器,然后第二个参数是移动的list,后面的参数是迭代器,可以移动一个元素,也可以移动多个连续的元素。
顺序容器之间的常见问题
vector和deque的区别
  • vector的内存是连续的,deque的内存是间隔连续的,每一个二维数组是连续的
  • vector的前插和中插的时间复杂度是0(n),后插的时间复杂度是0(1),deque的前插,后插是0(1),中插是0(n)。
  • 因为vector的内存是连续的,因此vector插入效率比deque的插入效率高。
vector和list的区别
  • 对于数组和链表的选择,特征比较明显,随机访问多的选择vector,增删多的选择list。
迭代器失效的问题
  • 迭代器遍历容器时,是不允许连续删除或者插入元素的,因为每次插入或者删除一个元素后,迭代器就失效了,因此如果连续删除或者插入元素,就需要进行迭代器的的更新。

容器适配器

  • 之所以叫容器适配器,是因为容器适配器没有自己的底层数据结构,底层使用是顺序容器。
stack栈
  • 底层默认依赖deque实现的。
  • deque的二维内存是按照固定大小开辟的,相比vector初始内存使用率高
  • deque是分段开辟内存的,内存利用率高。
函数方法使用方式功能描述
pushpush(val)入栈
poppop()出栈
emptyempty()判空
sizesize()元素个数
toptop()获取栈顶元素
queue队列
  • 底层默认duque实现的。
  • deque的二维内存是按照固定大小开辟的,相比vector初始内存使用率高
  • deque是分段开辟内存的,内存利用率高。
  • deque本身是双端队列,因此更适合queue队列
函数接口使用方式功能描述
pushpush(val)入队列
frontfront()查看队头元素
poppop()出队列
backback()查看队尾元素
sizesize()队列元素个数
emptyempty()判空
priority_queue优先级队列
  • 底层使用的是vector,因为优先级队列底层是大根堆,因此需要使用vector的连续内存实现,需要按照下标计算孩子节点位置。
函数接口使用方式功能描述
pushpush(val)入队列
poppop()出队列
toptop()队头元素
sizesize()元素个数
emptyempty()判空
  • 因此底层是默认大根堆,因此在可以解决top k 问题,也可以在定义时修改为小根堆。
template<class _Ty,class _Container = vector<_Ty>,
			class _Pr = less<typename _Container::value_type> >
class priority_queue;
// 可以看到priority_queue的底层默认使用的是less,less是 a < b
priority_queue<int> que1;
// 修改为小根堆
priority_queue<int,vector<int>,greater<int>> que2; // greater是 a > b 
  • less 和 greater都是类模板,实现的是operator()小括号运算符重载函数。
int main()
{
	priority_queue<int,vector<int>,greater<int>> que; // 大到小
	priority_queue<int> que1;  // 默认小到大
	for (int i = 0; i < 10; ++i)
	{
		que.push(rand() % 100);
		que1.push(rand() % 100);
	}
    
	while (!que1.empty())
	{
		cout << que1.top() << " ";
		que1.pop();
	}
	cout << endl;
    
	while (!que.empty())
	{
		cout << que.top() << " ";
		que.pop();
	}
	cout << endl;
    
}

/*
91 67 64 58 45 42 36 27 24 0
5 27 34 41 61 62 69 78 81 95
*/

关联容器

无序关联容器(C++11)
  • 底层是链式哈希表,增删查接近O(1),效率非常高。
容器名称容器说明
unordered_map无序单重映射表
unordered_set无序单重集合
unordered_multimap无序多重映射表
unordered_multiset无序多重集合
  • 常用的是unrodered_map 和 unordered_set
  • unordered_map常用于查重,unordered_set常用于去重。
  • unrodered_set常用函数:insert(val),find(val), 返回的都是迭代器,erase(val),erase(it)。
  • unrodered_map常用函数:insert({key,val}),find(key)返回迭代器,erase(key),erase(it),[]中括号运算符重载函数,map[key] = val:如果key不在插入key,如果key在修改val,并且返回key对应的val。
有序关联容器
  • 底层是红黑树,增删查是O( log2(n) ),效率很高。
容器名称容器说明
map有序单重映射表
set有序单重集合
multimap有序多重映射表
multiset有序多重集合

对于关联容器大家可以自行查找其他函数方法,这里不做多的解释


新手上路,如有错去,请指出!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习并掌握C++2.0(11+14+17+20)的新特性,学习线程及线程池的应用 ---------------------------------------------------给小白学员的3年学习路径及计划技术方面分三块:1.纯开发技术方向2.音视频流媒体专业方向3.项目实战---------------------------------------------------1.纯开发技术方向(1) C++必须要过硬(至少学会10本经典好书)(2) 系统级编程(Windows、Linux),必须特别熟练系统API,灵活运用(3) 框架与工具(Qt、MFC):必须精通其中一种。(4) 架构与设计模式:需要提升一个高度,不再是简单的编码,而是思维模式。(5) 驱动级别(如果有兴趣,可以深入到驱动级:包括Windows、Linux)(6) 最好学习点Java+Html+javascript等WEB技术。2.音视频流媒体专业方向(1) 音视频流媒体基础理论:   必须认真学会,否则看代码就是看天书(2) 编解码方向:精通h.264,h.265(hevc), 包括理论和各个开源库(ffmpeg,libx264,libx265,...)。(3) 直播方向:  精通各种直播协议(rtsp,rtmp,hls,http-flv,...), 钻研各个开源库(live555,darwin,srs,zlmediakit,crtmpserver,...)(4) 视频监控:  理论+开源库(onvif+281818)(EasyMonitor、iSpy、ZoneMinder(web)、...) 3.项目实战(1) Qt项目:  至少要亲手练习10个实战项目(网络服务器、多线程、数据库、图像处理、多人聊天、等等)(2)音视频项目:包括编解码、视频监控、直播等各个方向,都需要亲手实战项目,包括视频服务器、后台管理系统、前端播放器(多端)---------------------------------------------------  第1章 C++11新特性 41). nullptr关键字与新语法 42). auto和decltype类型推导 6 auto讲解 6 auto示例 7 decltype 83). for区间迭代 94). 初始化列表 105). 模板增强 11外部模板 11类型别名模板 12默认模板参数 126). 构造函数 13委托构造 13继承构造 147). Lambda 表达式 158). 新增容器 20std::array 20std::forward_list 21无序容器 22元组 std::tuple 239). 正则表达式 2610). 语言级线程支持 28多线程库简介 2811). 右值引用和move语义 31右值引用和move语义 32转移左值 3412). constexpr 35第2章 C++14新特性 36Lambda 函数 36类型推导 37返回值类型推导(Return type deduction) 37泛型lambda 39[[弃用的]]  [[deprecated]]属性 40二进制数字和数字分隔符 41第3章 C++17新特性 42安装GCC10.2 42安装msys2-x86_64-20200720 42更新镜像 42更新软件库 43安装 MinGW64 等必要的软件 43环境变量Path 43编译命令 43constexpr 44typename 45折叠表达式 47结构化绑定 48条件分支语句初始化 49聚合初始化 50嵌套命名空间 52lambda表达式捕获*this的值 53改写/继承构造函数 54用auto作为非类型模板参数 55__has_include 56fallthrough 57nodiscard 57maybe_unused 58第4章 C++20新特性 59编译命令 59concept 59typename 60explicit 61constinit 62位域变量的默认成员初始化 62指定初始化 63基于范围的for循环初始化 64放宽基于范围的for循环,新增自定义范围方法 65嵌套内联命名空间 66允许用圆括弧的值进行聚合初始化 67unicode字符串字面量 68允许转换成未知边界的数组 68likely和unlikely 69第5章 C++2.0(11/14/17/20)总结与分析 705.1 C语言与C++ 715.2 语言可用性的强化 725.2.1 常量 725.2.2 变量及其初始化 735.2.3 类型推导 745.2.4 控制流 765.2.5 模板 775.2.6 面向对象 815.3 语言运行期的强化 835.3.1 Lambda 表达式 835.3.2 右值引用 865.4 容器 885.4.1 线性容器 885.4.2 无序容器 895.4.3 元组 895.5 智能指针与内存管理 905.5.1 RAII 与引用计数 905.5.2 std::shared_ptr 905.5.3 std::unique_ptr 915.5.4 std::weak_ptr 91第6章 C++2.0多线程原理与实战 93什么是并发 93并发的方式 93为什么使用并发 95线程简介 96创建线程的三种方式 971. 通过函数 972.通过类对象创建线程 993.通过lambda表达式创建线程 101thread线程的使用 101互斥量与临界区 105期物Future 111条件变量 112原子操作 114内存模型 118第7章 C++2.0线程池原理与实战 120线程与线程池的基本原理 1201)、线程 1202)、线程的生命周期 1213)、什么是单线程和多线程 1214)、线程池 1225)、四种常见的线程池 123线程池的架构与流程 123线程池代码实战 125    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值