STL六大组件
容器(containers)、算法(algorithms)、迭代器(iterators)、适配器/配接器(adapters)、仿函数(functors)、配置器(allocators)
文章目录
Array
STL定义:
template < class T, size_t N> class array;
- 创建数组:
std::array<typename, size> array
如:array<int, 10> arr; - 获取首个元素以及末尾后一个元素的迭代器:
begin()、end() - 获取首个元素前一个位置,以及末尾元素的迭代器:(反向迭代器)
rbegin()、rend() - 返回常量迭代器(c):迭代器可以++或–,但是不能修改它的指向
cbegin()、cend()、crbegin()、crend() - 获取数组中的元素数量、判断数组是否为空
size()、max_size()、empty():对于Array而言,max_size()==size(),因为数组的长度是固定的。对于其它容器而言,size()表示当前容器中元素数量,max_size()表示容器可以设置的最大容量 - 返回数组第n个位置的元素的引用
重载下标运算符[]:operator[]:用法array[n]、at:用法array.at(n)。区别在于下标运算符不会检测边界,下标溢出会报错,而at会检测边界。 - 返回数组中第一个元素、最后一个元素的引用
front():begin()返回的是迭代器,还需要解引用 *iterator,而front()直接返回第一个元素的引用;back()返回最后一个元素的引用。
front() < 对应>back();begin() <对应> end(); - 返回指向array首个元素的指针
data():返回指向数组对象中第一个元素的指针。与begin()的区别在于,std::array::begin 返回的是 iterator迭代器,而std::array::data返回的是pointer指针。由此可以看出,迭代器用法接近指针,但是实际还有一些区别。typeid(begin()).name()返回N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE,而typeid(data()).name()返回Pi表示int类型的Pointer。 - 使用指定元素填充数组,将指定val设置为数组对象中所有元素的值
fill(const value_type& val):用法:array.fill(5); - 通过x的内容交换数组的内容,只能用于大小和类型相同的两个数组
array1.swap(array2);交换两个数组的元素,要求typename 和 size相同 - 获取指定下标元素的引用
std::get<i>(array);返回指定下标i元素的i引用。结果等价于下标和at取引用,不同在于std::get<i>(array)是一个辅助函数 - 关系操作符:如> < !=
array1 < array2:依次比较数组每个元素的大小。和swap()函数一样,关系运算符要求typename和size相同,即两个数组的格式相同。关系操作符是非成员方法。
Vector
Vector的定义:
template < class T , class Alloc = allocator<T>> class vector;
allocator是配置器,默认参数allocator<auto T>:负责内存的分配和释放、对象的构造和析构。可以自定义my_allocator类,为vector分配空间。
Array是不可变大小的数组,而vector是可以改变大小的数组。与array一样,vector也是序列容器,为元素提供连续的存储位置,即可以使用指针的偏移来访问元素。
在内部,vector使用动态分配的数组来存储元素,这个数组可能需要重新分配,以便在插入新元素时调整数组大小capacity。就处理时间而言,每次将元素拷贝到新数组比较耗时,因此每次需要扩增时,将容量扩大一倍。
此外,如果已知元素规模大小,可以指定vector的容量。
与其它动态序列容器(deque、list、forward_list)相比,vector能够高效访问元素,并相对有效地从末尾添加或删除元素。而对于除末尾之外其它位置的元素插入/删除,需要移动数据,效果较差。
针对vector的各种常见操作复杂度:
随机访问:O(1)
尾部增删元素:O(1),因为找到尾部很快,并且不需要移动元素
增删元素(其他位置):O(n),元素到尾部的线性距离,插入元素位置以后的迭代器失效。
- vector的构造器
①默认构造器/空构造:vector<typename> vec;
②拷贝构造函数:vector<typename> vec2(vec1);
③范围构造器range:vector<typename> vec2(vec1.begin(), vec1.end());
④fill构造器:指定填充n个val:vector<typename>(n, val);
⑤列表初始化构造:vector<string> vec_str = {“list”, “initializer”};
⑥移动move构造函数:copy构造需要创建对象,并析构该对象,而move构造将左值引用转变为右值引用,避免不必要的拷贝操作。(左值可以赋值,右值不可赋值(即将销毁的临时对象)
std::move(vec);此外emplace_back()也可以避免元素的拷贝。**move函数需要标记为noexcept,否则即使声明move,也有可能调用拷贝构造,进行异常保证。**为了提高性能,可以将move标记为 noexcept(指示函数是否可能抛出异常,如果使用noexcept标记,告诉编译器,该函数不会发生异常,有利于编译器对程序左更多优化。如果发生了异常,会调用std::terminate()函数,该函数内部调用std::abort()终止程序)。 - vector的析构函数
~vector();销毁容器对象,在每个包含的元素上调用allocator_traits::destory,并使用其分配器释放矢量分配的所有存储容量 - operator=重载赋值运算符
将新内容分配给容器,替换当前内容,并适当地修改其大小:vec2 = vec1; - begin()、end()、rbegin()、rend()、cbegin()、cend()、crbegin()、crend
分别返回数组首元素迭代器、末尾元素后一个位置的迭代器、反向迭代器、不可改变指向的迭代器(可以++ --) - size()、max_size()、capacity()
size()返回数组中元素的数量
max_size()返回容器可以达到的潜在最大大小,由已知的系统或库进行限制
capacity()返回容器大小,即当前vector所占存储空间大小。当size()==capacity()时,再插入元素需要动态扩容。 - empty()、resize()
empty()判断容器是否为空(size()==0);
resize(n);如果n小于当前容器size,则内容被缩小到前n个元素,将其余元素删除,如果n大于当前容器size,则通过在末尾插入元素扩展到n,如果n超过了capacity,那么需要动态扩容。resize(n, val)可以指定插入的元素值 - reverse(size_type n);
指明请求vector容量应该至少满足n,如果n大于capacity,会导致容器重新分配存储容量,除此以外,不会改变capacity,这个函数不会改变size(),也不改变元素。 - void shrink_to_fit()
要求容器减少capacity以适应期尺寸,即使capacity==size - operator[]、at()、front()、back()、data()、assign()
分别返回指定下标的引用、首元素、末尾元素的引用、指向首元素的指针。
assign()有三种用法:range、fill、initializer list:
assign(first_iter, end_iter);注意,C++STL是左闭右开[first, end),左闭右开原则也可用于算法设计
assign(n, val);使用指定的数值分配给vector
assign(initializer list);将初始化列表中的元素分配给vector。assign会改变容器的size,但不会改变capacity() - pop_back()、push_back()、emplace_back()、insert()、erase()
pop_back()、push_back()是insert和erase的特殊情况,insert(itr, n, val)在指定位置开始插入n个val;erase(itr_first, itr_end);end可省略,删除该迭代器指示区域的单个元素,左闭右开 - swap()
vec1.swap(vec2),与array不同,vector可以交换不同尺寸vector的内容,但是类型必须相同 - 删除vector中所有元素,留下size=0的容器
vec.clear() - emplace()、emplace_back()
可以避免额外的复制和移动操作,插入的元素通过调用allocator_traits::construct转换args来创建。 - 返回与vector关联的构造器对象的副本
allocator_type get_allocator() const noexcept;
用法:int* p = v.get_allocator.allocate(n);获取vector的allocator,进行内存管理,地址由相同类型的指针指向。 - deallocate()、destory()
destoy():负责调用析构函数,销毁相应内存上的内容,销毁后内存地址仍然保留
deallocate():负责释放内存(此时相应内存中的值在此之前应调用destory销毁,来将内存返回给系统,代表这部分地址使用引用-1。
deque(['dek])
deque双端队列:double-ended queue,deque是具有动态大小的序列容器,可以在两端扩展或收缩。
deque有类似于vector的功能,但在序列开始处也可以高效地插入和删除元素,但deque不保证所有元素存储在连续的存储区域:deque通过偏移指向另一个元素的指针访问元素会导致未定义行为。
vector使用单个数组,存储在连续区域内,因此有时需要动态扩容重新分配内存。而deque的元素可以分配在不同的块的容器,容器在内部保存必要的信息以提供对其任何元素的持续时间和统一的顺序接口的直接访问。
因此,deque会比vector复杂一些,在除两端位置以外插入和删除的表现更差(虽然仍是O(n))。
随机访问:O(1);
结尾和开头增删:O(1);
插入或删除:O(n);
- deque的构造函数
template<class T, class Alloc = allocator<T>> class deque;
①空构造:deque<typename> d1;
②fill填充构造:deque<typename>d2;
③范围构造:deque<typename>d3(d2.begin(), d2.end());
④列表初始化:deque<typename>d4 = {1, 2, 3};
⑤拷贝初始化:deque<typename>d5(d4);
⑥移动构造:deque<typename>d6(std::move(d5)); - 添加元素
push_back()在容器末尾添加新元素,val被复制到信道元素位置
push_front()在容器开始插入一个新元素,以上两种操作都会导致容器size + 1;
emplace_front():在容器开头插入一个新元素;
emplace_back():在容器末尾插入新元素 - 删除元素
pop_back()删除容器中的最后一个元素,size-1;
pop_front()删除容器中的第一个元素
forward_list
forward_list(单向链表)是序列容器,允许在序列的任何地方以常数时间进行插入和删除操作。
forward_list(单向链表)被实现为单链表:单链表可以将它们包含的每个元素存储在不同和不相关的存储位置中,通过关联到序列下一个元素的链接来保留排序。
forward_list和list的区别:list是双向链表,每个元素保留两个链接,一个指向下一个元素,一个指向前一个元素。 而forward_list 单向链表,只保留到下一个元素的链接。 forward_list通常在插入、提取和移动容器内元素的表现较好,因此更适合密集使用这些特性的算法,如排序。而缺点在于无法直接访问,访问某位置元素,需要从开始位置迭代到该位置,这需要线性时间。
- 构造函数
①默认构造/空构造:forward_list<typename> forward_list1;
②fill填充构造:forward_list<typename> forward_list2;
③range范围构造:forward_list<typename>(f1.begin(), f1.end());
④拷贝构造:forward_list<typename>f4(f2);
⑤移动构造:forward_list<typename>f5(f4);
⑥列表初始化:forward_list<typename>f6 = {elem1, elem2…}; - 析构函数
~forward_list(); - 返回指向容器中第一个元素之前的位置的迭代器。
before_begin():返回的迭代器不能被解除引用,该迭代器用于指示动作的开始位置。
cbefore_begin():返回指向容器第一个元素之前位置的const_interator;该迭代器可以增加或减少,但不能修改指向
list
STL定义:
template< class T, class Alloc = allocator<T>> class list;C++STL容器需要allocator管理内存。
List是一种常用的序列容器,保存相同数据类型的对象。list被实现为双向链表,因此其提供对数据的双向顺序访问。
双向链表/单向链表的元素可以分配在不同的内存块中,容器存储必要的信息以允许顺序访问它的数据。双向链表可以根据需要从两端收缩或扩展。
list只支持双向顺序访问,不支持快速随机访问,能够以O(1)时间在序列任何位置进行插入和删除。
零大小的list也是有效的。此时list.begin()和list.end()指向相同的位置,但front()和back()是未定义的。
- list的构造方法
①默认构造/空构造:std::list<typename> l1;
②fill构造:list<typename>l2(n, val);
③range构造:list<typename>l3(l2.begin(), l2.end());
④copy构造:list<typename>l4(l3);
⑤move构造:list<typename>l5(std::move(l4));
⑥initializer list构造:list<typename>l6 = {e1, …en}; - 析构函数
~list();通过释放内存来销毁容器对象 - 成员函数
assign的三种用法:range、fill、initializer list分配新值
assign(l1.begin, l1.end()、assign(n, val)、assign({1, 2});assign会改变容器的size,list没有capacity
front()、back():分别返回双向链表第一个元素和最后一个元素的引用,list为空时是未定义的。
begin()、end():分别返回指向首元素、末尾元素后一个位置的双向迭代器:不支持begin()+n的随机访问,但是可以++begin(),–begin()
cbegin()、cend():返回不可改变指向的双向迭代器;
rbegin()、rend()、crbegin()、crend():对应的反向迭代器;
clear():删除list元素,并将size()设置为0;
emplace():emplace(itr_position, args);在迭代器指向的指定位置插入元素,并返回指向该位置的迭代器;
emplace_front(val)、emplace_back()、push_back()、push_front():分别在list的头、尾部插入新元素;
pop_back()、pop_front():分别删除尾、头部元素,没有返回值;
remove(val)、remove_if(pred):删除list中指定元素、remove_if()的参数是一元谓词。
resize(n, val):改变list的大小;
max_size():编译器允许的最大数量;
insert(itr_position, n, val):在指定位置插入n个val元素;
insert(itr_pos, begin(), end()):在指定位置range插入;
insert(itr_pos, move({1,2}));在指定位置插入初始化列表;
merge():l1.merge(l2):将两个有序链表合并,合并的结果仍是有序链表。可以自定义比较规则,如:l1.merge(l2, comp);
erase(itr):删除迭代器指向位置的元素:和remove()区别在于,remove删除指定元素,因此如果删除自定义数据类型,需要operator==重载比较运算符
sort()、reverse():排序链表,可以指定比较规则、翻转链表。
operator=:赋值运算符
unique:删除list中的重复元素:list.unique();
splice(itr_pos, list/{}/move(list):splice将元素插入到指定位置,和insert的区别是,insert是复制后插入,splice是直接将对象拼接到指定位置。与merge区别是merge有序。
swap():交换两个链表的内容,不改变元素顺序。
map
STL定义:
templae<class Key, class T, class Compare = less<key>, class Alloc = allocator<pair<const Key, T>>> class map;
从定义可以看出:配置器通过配置pair构造map,因此insert和make_pair(key, T)方便插入元素;
map是关联容器,按照特定顺序存储key-value(键值对),在map中,键值对通过pair存储:typedef pair<const Key, T> value_type;
Map映射通常实现为二叉搜索树
- 构造函数
①空构造/默认构造:map<typename_key, typename_value> map1;
②range范围构造:map<key_t, value_t> map2(map1.begin(), map1.end());
③拷贝构造:map<key_t, value_t>map3(map1);
④移动构造:可以自定义键值比较,默认是按照key由小到大排列。 常用的unordered_map<key, value>是无序的。
⑤列表初始化:map<int, char> map5 = {{1, ‘a’}, {2, ‘c’}};
⑥直接赋值:map[key] = value;如map5[1] = ‘c’; - begin()、end()
返回指向map第一个元素、末尾元素后一个位置的迭代器 - key_comp、value_comp
key_comp()返回容器用于比较键的比较对象的副本;
value_comp()返回可用于比较两个元素的比较对象,以及第一个元素的键是否在第二个元素之前。
用法:auto comp = map.key_comp();
comp(begin()->first, end()->first),key_comp()返回的是key比较对象。
auto comp = map.value_comp();
comp(*begin(),*rbegin())
value_comp()返回的是用于比较value_type的对象 - find()、count()
find(key):查找与key一致的元素,找到返回指向该元素的迭代器(返回第一个出现的位置的迭代器),如果没有返回end()
count(key):搜索等于key的元素,并返回匹配的个数。由于map中key不允许重复,所以count(key)只能返回1或0。
注意:map不允许key重复,multimap允许key重复 - map的插入
①方法一:数组形式:map[key] = value;
②方法二:使用insert插入pair数据:map.insert(pair<key_t, value_t>(key, value));可以看出,在插入中构造了一个pair
③方法三:使用insert插入一个value_type:map.insert(map<key_t, value_t>::value_type(key, value));
④方法四:利用insert和make_pair插入数据:map.insert(make_pair(key, vlaue));
常用insert(make_pair(key,value)); - map的删除
erase(key):删除指定key的元素。 - lower_bound、upper_bound、equal_range
lower_bound(key):将迭代器返回下限,返回第一个指向容器中key>=输入key的元素迭代器
upper_bound(key):该迭代器返回指向第一个容器中的key>输入key的位置的迭代器
equal_range(key):**返回一个pair<first,second>,可以认为是lower_bound和upper_bound的返回值。pair的输出:pair.first->first(分别是pair的第一个元素(迭代器)的第一个(key)
pair.first->second:pair第一个元素的第二个(value)
**
map比较特别的是find:查找指定find(key)的value
multimap
STL定义:
template< class Key, class T, class Compare = less<Key>, class Alloc = Allocator<pari<const Key, T>
- multimap的构造函数:
①空构造/默认构造:multimap<type_key, type_value>multimap1
②range构造:multimap<type_key, type_value>m2(m1.begin(), m1.end());
③copy构造:multimap<type_key, type_value>m3(m2);
④move构造:multimap<type_key, type_value>m4(move(m3));
⑤initializer list构造:两种形式:直接通过{}赋值,或={}赋值
multimap<type_key, type_value>m5{{key, value}};
multimap<type_key, type_value> m6 = {{key, value}};
初始化列表中的键值对,用{}大括号表示,用逗号分开key和value,如{{k1, v1}, {k2, v2}};
set
STL定义:
template<class Key, class Compare = std::less<Key>, class Alloc = std::allocator<Key>>> class set;key可以是任意数据类型,包括用户自定义数据类型。
Set有序,需要指定比较规则std::less<tpye>或std::greater<t>,默认从小到大。
set和map一样,也是关联容器,但set包含有序的key类型的唯一对象,每个对象只能出现一次。Set中的元素不可修改,即元素始终为const,但是可以从容器中移除再插入
Set容器访问单个元素通常比unordered_set容器慢,但是允许顺序迭代
- set的构造函数
①默认构造/空构造:set<type> s1;
②range:set<type> s2(s1.begin(), s1.end());
③copy:set<type> s3(s2));
④move:set<type> s4(move(s3));
⑤initializer list:set<type> s5 = {e1, e2, e3}; - set的函数
find(key);查找某个key是否在set中,如果存在的话就返回指向该位置的迭代器,不存在返回end();
count();统计指定元素的数量,由于count不允许重复,因此返回0或1。 - 插入
insert(key);emplace(key);返回指向插入元素的迭代器,和true,pair对象;
emplace_hint(itr_pos, key);需要传入插入位置的迭代器,返回的不是pair,只是插入元素的迭代器,插入失败的话指向与新元素相同的元素
erase()三种用法:
erase(key):删除指定key值元素
erase(itr):删除迭代器指向位置的元素
erase(begin(), end()):删除指定范围的元素
unordered_set
STL定义:
template < class Key, class Hash = hash<key>, class pred = equal_to<key>, class Alloc = allocator<key>> class unordered_map;
可以看出,unordered_set也是基于哈希表实现的,插入、删除、查找的时间复杂度为O(1)
- 特殊函数
无序关联容器基于哈希表实现,因此可以具有哈希表相关的操作
bucket_count():返回哈希桶的数量。哈希桶的数量:就是哈希表数组的元素个数(链地址,桶5如果由元素,就插入到桶5的链表中)。
bucket(key);返回值为key的元素对应的桶号。无效key会导致未定义的行为,因此最好使用itr迭代器解引用获取的有效值传参
max_bucket_count():类似于max_size():编译器允许的最大桶数量;
bucket_size(number);返回编号为number的桶内元素个数。非法参数会导致未定义行为。 - 哈希策略
load_factor();返回装在因子:哈希表中元素填满程度的度量。是已存元素个数与哈比表长度的比值。
装载因子过高:减少了空间开销,提高了空间利用率,但增加了查询时间成本。空闲位置少,散列冲突的概率增加,散列表的性能会下降;
装载因子过低:空间利用率低,提高了rehash再哈希的次数。
max_load_factor(optional new_factor):获取或设置最大的加载因子。new_factor是可选的,设置合理的装载因子,能够提高哈希表的效率和性能。
rehash(n);将容器中桶的数量设置为n或更多。
解决哈希冲突的一种方法。当哈希表的负载因子达到一定阈值时,创建一个新的更大或更小的哈希表,然后重新计算原来哈希表中的元素值存入新的哈希表中,降低哈希碰撞概率。
reverse(n);将容器中的桶数量bucket_count设置为最适合包含最少n个元素的值。
hash_function(),返回哈希函数。
key_eq();返回一个key比较对象,用于比较set中的元素是否相等。
unordered_map
STL定义:
template < class Key, class T, class Hash = hash<key>, class Pred = equal_to<Key>, class Alloc = allocator<pair<const Key, T>>> class unordered_map;
无序映射:只允许单个值与唯一的key关联,实现了[]下标运算符,允许通过键值作为参数访问。
与map不同,无序映射不会根据key或value的特定规则对元素排序,而是根据其哈希值组成桶(哈希桶:就是解决hash地址冲突的链地址法),允许通过键值随机访问元素。
set和vector的区别:
1)vector数据可以重复、set不可以重复
2)vector数据无序,set集合有序
3)vector插入时间复杂度是O(1):尾插;set插入的时间复杂度是O(log n):先排序(寻找插入位置)
4)vector尾部插入快;set在中间(中间值)插入较快。
无序容器
unordered_set、unordered_map、unordered_multiset、unordered_multimap
以上四种容器都是通过哈希表实现的。
map和unordered_map区别:map通过红黑树(严格的平衡二叉排序树)实现,红黑树具有自动排序的功能,因此map内部元素是有序的,而unordered_map内部实现了一个哈希表,查找的时间复杂度是O(1)
##queue
STL定义:
template<class T, class Container = deque<T>>calss queue;
可以看出,队列是基于双端队列deque实现的
queue:队尾进、队首出。
- 构造函数
①queue<type> q1;
②queue<type> q2(q1);
③queue<type> q3(move(q2));
④queue<type> q4 = {e1, en}; - 析构函数
~queue(); - 成员函数
back()、front():返回队列最后一个元素、第一个元素的引用(队尾、队头);
emplace()、push():队尾插入新元素
pop():删除队首元素,没有返回值,对该值感兴趣需要front()提取;
empty()、size():判断是否为空,获取元素总数;
operator=:重载赋值运算符 - 非成员函数
重载关系运算符,比较队列是否相等、大小:
operator==、operator!=、operator<、operator<=、operator>、operator>=。
swap():用法:swap(q1, q2);成员函数也有一个swap()算法,q1.swap(q2);
priority_queue
STL定义:
template<class T, class Container = vector<T>, class Compare = less<typename Container::value_type> > class priority_queue;
由:默认Container可以看出,priority_queue默认是基于vector实现的,可以调整第二个类型参数来重载默认容器类型。
priority_queue优先队列是保持优先级的队列数据结构。优先队列类似堆(有序),其中元素可以按任何顺序插入,并且总是首先检索max heap元素。
- 构造函数
①默认构造/空构造:priority_queue<typename> pq1;
②copy构造:priorty_queue<typename>pq2(pq1);
③move构造:priorty_queue<typename> pq3 (move(pq2));
④range构造:priorty_queue<typename> pq4(first, last, const Compare& comp, const Conatainer& cntr);
Compare:用于排序priority_queue的对象,可以是一个函数指针或函数对象,可以自定义
cntr:容器对象:优先队列的底层容器类型,默认为vector。
⑤initializer list:priority_queue<typename> q(less<typename>(), initial list); - 成员函数
优先队列的插入:
emplace():该函数有效地调用底层容器的emplace_back()函数
push():调用底层容器的push_back()
pop():删除队首元素;
top():返回队首元素:优先队列中:优先级较高的在队首,默认less算子,如果数值大优先级就大。
size():返回元素个数
empty();判断是否为空
pop之前要判断!empty()
swap()交换优先队列内容
priority_queue和queue的区别在于:①pq基于vector实现,q基于deque实现;②pq是有序的,优先级较高的排在队首优先出队,而q是无序的,严格遵循队尾进队首出(FIFO)
stack
STL定义:
template< class T, class Container = deque<T>> class stack;
堆栈:设计在LIFO(后进先出)上下文中运行的数据结构。在堆栈中,元素被插入以及仅从一端移除(仅允许在一端进行插入和删除操作,这一端被称为栈顶,相应的,另一端是栈底)栈顶和栈底是相对的,并不是固定的位置,在栈顶插入、删除意味着:栈顶指向当前栈最新的数据,栈底指向最旧的数据。
Stack是容器适配器(adapter)。容器是保存相同数据类型的对象。可以从不同的序列创建堆栈,默认底层容器是deque。容器适配器不支持迭代器,因此无法使用迭代器操作数据,但支持push()和pop()插入和删除数据。
- 构造函数:
①默认构造/空构造:stack<typename> s1;
②copy:stack<typename> s2(s1);
③move:stack<typename> s3(move(s2)); - 析构函数
~stack(); - 成员函数
emplace()、push():在栈顶插入新元素。 调用底层容器的方法;
pop():移除栈顶元素,没有返回值,需要结合top使用。
top():返回栈顶元素的引用
size():返回栈内元素
swap():交换两个堆栈的内容
为了限制操作:容器适配器是没有迭代器的,只能通过一端的元素提取和删除(移动指针)top()+pop()来遍历元素。 - 非成员函数
关系操作符重载:
operator==、operator!=、operator<、operator>、operator<=、operator>=
swap(s1, s2);交换两个堆栈内容
容器适配器
容器适配器是对特定容器的封装:deque->queue、stack;vector->priority_queue;
容器适配器和容器的区别:容器适配器改变了容器的接口,提供了有限的操作,实现不一样的功能。
①容器适配器是对容器的封装,仅提供特定的接口,限制适配器的行为;
②容器适配器不支持迭代器和算法,因为它们不允许访问容器中的任意元素,只能访问容器的一端(栈顶、队首);
③容器适配器可以指定底层容器的类型:修改默认底层容器,根据不同的需求选择不同的底层容器,提高效率。
STL提供三种容器适配器:stack、queue、priority_queue。
tuple
元组tuple:元组是一个能够容纳元素集合的对象,每个元素可以是不同的类型:std::tuple<type1, type2, type3…> tup1;多个不同数据类型的元素的集合:元组
- 构造函数
①默认/空构造:std::tuple<t1, t2, t3…>t1;
②拷贝构造:std::tuple<t1, t2, …tn>t2(t1);
③有参构造:std::tuple<int, char>(10, ‘a’);
④通过make_tuple()移动构造:这里make_tuple创建的是临时变量 std::tuple<t1,…tn>t4(std::make_tuple(val1,valn)); - 元素的访问
使用std::get获取元组指定位置的元素,使用get是因为元组没有[]下标运算符,如果愿意的话可以自己重载:std::get<0>(tuple)
get的用法:std::get<index>(object)
pair
pair:对;将一对值结合在一起,这些值可以是不同的类型。通过公有的成员变量first、second访问:如pair.first
pair是tuple的特例(仅有两个参数)
pair的实现是一个结构体,默认public,所以可以直接访问成员变量first second
- 构造函数
①默认/空构造:std::pair<t1, t2> p1;
②赋值/有参构造:std::pair<t1, t2>p2(val1, val2);
③拷贝构造:std::pair<t1, t2> p3(p2);
④移动构造:std::pair<t1, t2> p4(move(p3));
⑤利用make_pair()也是右值 构造:std::pair<t1, t2> p5(std::make_pair(val1, val2));
bitset
STL定义:
template < size_t N> class bitset;
bitset表示N位的固定大小序列,并存储值0或1。零表示值为假或位未设置,一表示值为真或位已设置。bitset模拟节省空间的布尔值数组,并且每个元素只占一位。
bitset的size在编译时是固定的,不可以改变。
- 构造函数
①bitset<n> b;创建n长度的bitset容器并将其初始化为0;
②bitset<n>b(val);创建n长度的bitset容器,并用val填充状态位:如8 == 1000。
③利用字符串构建bitset:bitset<n>b(str ,pos_first, n);pos_first默认为0,n默认为传入字符串长度。
bitset的初始化,是从低位向高位填充。 - 关系运算
operator&=按位与;
operator|=按位或;
operator^=按位异或;
operator<<=对当前位集对象按位左SHIFT操作;如0010 <<= 1 得到0100;
operator>>=:按位
operator~:按位非
operator<<;左shift;
operator>>右shift;
<<= 和 <<的区别在于,前者改变当前对象,后者返回新的bitset对象
operator&;按位与,返回新对象;
operator|;
operator^;
同理:&= 和&的区别在于后面&生成新bitset对象
operator==;
operator!=;判断两个bitset是否相等。 - 成员方法:
all():测试是否设置了bitset的所有位;全为1时返回true
any():是否设置了至少一位;
count():计算已设置位数;
flip():切换所有位0变1,1变0
none():所有位是否未设置
operator[]:下标运算符,返回pos位的值/或引用;
reset():将所有位重置为0;reset(n)重置指定位置为0;
set():将所有位设置位1;可以传入参数set(n, 0/1)将指定位置设置为0或1;
size();返回bitset的大小;
test(n);判断第n位是否设置位1。等价于b[n]==1;
to_string();将bs对象转为字符串对象;
to_ullong();将bs对象转为无符号长长整数;
to_ulong();将bs对象转为无符号长整数 - 非成员方法
hash();根据bitset计算并返回一个哈希值。
algorithm
algorithm 算法库提供多种可能,可用于各种目的,如搜索、排序、计数、操作等。操作的范围为[first, end)左闭右开。要导入头文件#include<alorithm>
- 成员函数
adjacent_find(first(), end())查找两个相同的元素第一次出现的位置,返回该位置的迭代器,如果没有返回end();
all_of(begin(), end(), pred):如果全部满足一元谓词,返回true;
any_of(begin(), end(), pred):任一满足一元谓词,返回true;
binary_search(begin(), end(), val):使用二分查找查找元素,返回元素是否存在
copy(begin(), end(), result()):将[first, last)之间的元素复制到result处(result也是迭代器)
copy_backward(first, last, result):以反向顺序,将range内元素copy到reuslt迭代器指向位置;
copy_if(first, last, result, pred):将range内满足一元谓词要求的元素拷贝到result处;
copy_n(first, n, result):将first起始的n个元素拷贝到result处;
count(first, last, val):统计range内val出现的次数
count_if(first, last, val, pred):统计range内满足一元谓词的val出现的次数;
equal(v1.begin(), v1.end(), v2.begin()):比较两个序列是否相等:如果v2前部与v1相等,即使有剩余部分,也返回true;
equal(first, last, val):如果找到元素,返回子范围(返回的pair.first,pair.second)
fill(first, last, val):为指定范围分配val元素
fill(first, n ,val):为first开始的n个元素赋值为val
find(first, last, val):查找元素,如果找到返回元素第一次出现的迭代器,没有返回last;
find_end(first1, last1, first2, last2):返回指向[first1, last1]中最后一次出现[first2, last2]的第一个元素;
find_first_of(first1, last1, first2, last2):查找[f1, l1)范围内与[f2, l2)内任何元素匹配的第一个元素,如果没有返回last1;
find_if(first, last, pred):查找满足一元谓词的元素,返回指向第一个元素的迭代器,否则返回last;
find_if_not(first, last, pred):查找满足条件的元素最后一次出现的位置
for_each(first, last, func):将自定义的func作用于序列范围内所有元素:如:for_each(v.begin(), v.end(), print);这里的func可以是仿函数
includes(first1, last1, first2, last2, compare):判断2是否是1的子集;
仿函数也可以是谓词,仿函数:重载括号的类operator(),使用该类的重载()函数像函数一样classname()
仿函数和成员函数的区别:①当我们调用成员函数时,实际上是在访问类的一个属性(成员、成员函数、成员属性);而当我们使用()来调用仿函数时,实际是在执行类的一个操作,也就是operator()。这两种方式的本质不同,因此(class.func()调用成员函数,class()执行类操作)
inplace_merge(first,middle, last):三个参数为第一个序列起始位置、第二序列起始位置、第二序列末尾位置。合并后序列有序;
is_heap(first, last):判断给定的序列是否是最大堆(大顶堆);
is_heap_until(first, last);如果是大顶堆,返回last,否则返回第一个违反heap条件的元素;
is_sorted(first, last);判断范围内序列是否有序/已排序;
is_sorted_until(first, last);查找第一个未排序的元素;
is_partitioned(first, last, pred):parition(first, last, pred)可以对序列分区,因此可以根据相同的pred谓词判断序列是否已分区;
is_permutation(first1, last1, first2),判断范围1是否是范围2的排列(元素相同,顺序不同);
iter_swap(a, b):交换两个迭代器指向的对象的值:!没有改变迭代器指向,只是交换指向对象的值
lexicographical_compare(f1, l1, f2, l2):判断序列1是否按字典顺序小于序列2;
lower_bound(first, last, val):返回不小于(>=val)给定值的第一个元素的迭代器,没有满足条件则返回last;
upper_bound(first,last, val,comp);可以指定判断的谓词。
iterator
迭代器:操作类似指针的对象,可以使用++递增,使用/*解引用,并使用!=与另一个迭代器比较
需要导入迭代器:#include<iterator>
- 函数
begin()、end():分别返回指向序列第一个元素、末尾元素后一个位置的迭代器
advance(itr, n):将迭代器前进n个位置
distance(first, end)返回迭代器之间的距离
prev(itr, n):返回一个迭代器,该迭代器指向传入迭代器前n个位置的迭代器。
next(itr, n):返回一个迭代器,该迭代器指向传入迭代器后第n个位置的迭代器。
对于list而言,prev和next是循环的 - 迭代生成器
back_inserter(obj):返回在容器obj的末尾插入元素的back_insert_iterator;
inserter(obj, itr):返回一个insert_iterator将元素插入到指定位置; - 迭代器类别标签
input_iterator_tag:输入迭代器
output_iterator_tag:输出迭代器
forward_iterator_tag:前向迭代器:支持++,不支持–,支持 == !=,解引用 *itr、itr->m
bidirectional_iterator_tage:双向迭代器:比前向迭代器多了支持–
random_access_iterator_tag:随机访问迭代器:比双向迭代器多出功能:itr+/ - n随机访问(支持迭代器与整数值的算术运算)、或迭代器之间的减法运算 itr2 - itr1;支持不等式关系运算符itr1 < itr2、> 、<= 、 >=;支持复合赋值操作:itr +=n、-=;支持解偏移引用运算符itr[n]