C++ STL(Standard Template Library,标准模板库),提高程序开发效率和复用性。
STL包含6大组件:
容器: 用来管理、存储数据的一些数据结构,也叫做集合。
第1种容器:顺序/序列式容器,元素均有固定位置,插入元素,排列次序和插入顺序一致。
**1.vector(向量):**和数组类似,比数组更高级,可以动态扩展。拥有一段连续的内存空间,随机访问O(1),用下标直接访问。尾部插入也很方便O(1)。
中间插入、删除操作的话,O(n),效率较低,为了保存相对位置不变,需要所有元素都移动。
适用场景:对象简单,变化较小,需要频繁随机访问的场景。
vector的内存管理: 创建一个空对象时,会分配一个初始化大小的内存空间,为0或者预设大小(16)。维护一个capacity(容器),实时查看已分配的内存空间大小。
扩容方式:分配内存空间不足时,会去申请分配一个更大的内存(2倍),把原有数据拷贝到新空间,释放旧空间。
vector的一些操作: size:容器中的实际元素个数;capacity:容器可以容纳的最多的元素个数。
vector里的元素不能是引用,引用没有地址,vetcor元素要求在空间里顺序排列。
vector.clear(), 元素清空,但是vector申请的存储空间不变。capacity不变。
vector.swap(), 两个vector整体都交换,元素内容全部交换,size、capacity也全部交换。(可以用来解决内存释放问题,用一个空vector对象来交换)
vector使用时,会对内存造成严重浪费,一直在成倍申请,不会释放。要等到vector析构才会释放。
vector.shrink_to_fit(), 用来解决内存释放问题,将capacity置为0.
vector.resize(): 改变当前容器中的size个数,超过的话新增初始为0;小于的话则截断。
vector.reserve(): 改变当前容器的最大容量(capacity), 超过的话,重新分配内存,销毁之前拷贝新建。
判断相等: 两个vector可以用 == 判断相等,必须是相同数据类型且大小相同,逐元素比较,按照字典序。
vector申请的内存通常在堆空间上。 不在栈上。
vector通常需要动态管理其元素容量,堆上空间大小没限制,且由程序员控制生命周期,栈空间通常较小。
**2.list(链表):**底层是一个双向链表,内存空间不连续。
随机访问:不支持,每次都要从头指针开始遍历,查询效率很低O(n)。查询频繁适合用vector。
增加删除:适合频繁增加删除的场景。 在任意位置插入删除O(1)
3. array
#include< array >
C++ 11引入的固定大小的容器。空间大小在编译时就固定了,并且之后不能改变。
定义一个array数组时必须指明数据类型和个数:std::array<int,10> arr;
array的内存分配在栈上!!!
array随机访问的性能更好,比vector。。
判断相等: 两个array可以用 == 判断相等,必须是相同数据类型且大小相同,逐元素比较,按照字典序。
3.deque(双端队列,double-ended queue),
是一个功能强大的底层基础容器。
特点:
(1)双端操作,头部和尾部都可以高效插入删除,O(1)
(2)随机访问,下标访问类似vector,O(1),
内部结构:
由一块一块定量的连续空间构成,块内连续,块和块之间不连续。在头部或者尾部增加新空间时,偏会配置一个固定大小的块连续空间,然后把整个队列的首尾块空间连起来。比起vector,空间不需要成倍增加,头部插入也很高效。
应用场景: (1)既需要高效的在序列两端插入删除,也需要高效的随机访问,是vector和list的折中。
(2)deque的迭代器很复杂,如果不是必须在首尾两端进行操作的需求,不要用deque。
deque中没有capacity,因为deque容器没有容量限制, 可以无限量开辟缓冲区,只需要在中控区添加缓冲区地址用于维护缓冲区空间,所以deque没有容量的限制,可以无限扩展。
stack和queue:容器适配器。
STL中,默认是以deque作为底部容器来实现的。
stack:
在deque基础上,限制: 只能操作最顶部的元素(访问、增加、删除)。
严格遵循:后进先出。
适用场景:函数调用栈、括号匹配、表达式求值、回溯问题。
queue:
严格遵循先进先出。
限制性:只能在尾部增加元素、只能在头部移除元素,只能访问头部和尾部的元素。
适用场景: 消息队列,BFS-广度优先搜索。
string(字符串)底层也是自动双倍申请内存。
array,也可以看做序列式容器。
第2种容器:关联式容器,元素位置和插入次序无关,位置取决于元素值和排序准则。(默认字典序排序)
用仿函数,可以改变排序规则。
1.set(集合): 由红黑树实现,根据元素值自动排序(默认升序),每个元素只出现一次,不允许重复。每个key直接存储元素值。
set插入时,会返回插入结果,表示是否插入成功,重复的就不成功。(pair<x, bool>)
关键字即值,只保存关键字的容器。
插入时间复杂度:O(logN), 查找元素时间复杂度:O(logN), 删除元素时间复杂度:O(logN). 不允许修改元素值。
2.multiset(多重集合) 由红黑树实现。
和set唯一区别,可以插入重复数据,插入不检测数据是否重复。
插入:新插入相同数据,被放到相同数的右边,不改变已有数的相对顺序。
删除:如果删除的是重复元素,会默认全部删除ms.earse(x); 不想全部删除的话,传入一个指针即可,只删除当前指针的元素值。ms.erase(ms.lower_bound(x));
插入时间复杂度:O(logN), 查找元素时间复杂度:O(logN), 删除元素时间复杂度:O(logN). 不允许修改元素值。
3.map(映射),由红黑树实现。
容器存储的是pair对象,键值对。键值都可以是任意数据类型。容器自动根据key的大小自动排序(默认升序)。key的值不能重复,也不能修改。
重复插入key时,后面的插入不进来,会失败,所以只保留第一次插入的key-value对。
插入时间复杂度:O(logN), 查找元素时间复杂度:O(logN), 删除元素时间复杂度:O(logN).
4.multimap(多重映射),由红黑树实现。
和map唯一区别,可以插入重复数据,插入不检测数据是否重复。key相同时,自动对value也是按字典序自动排序。
插入时间复杂度:O(logN), 查找元素时间复杂度:O(logN), 删除元素时间复杂度:O(logN).
以上数据结构查询效率不够快,O(logN),为了提高查询效率,C++11推出了unordered_map和unordered_set。
基于哈希表的map和set
哈希表原理