1、STL简介
STL提供六大组件,彼此可以组合套用:
(1)容器:容器用来存放数据,从实现的角度看,STL容器是一种类模板,包括vector、list、deque、set、map等。
(2)算法:STL算法是一种函数模板,各种常用的算法如sort、search、copy、erase等。
(3)迭代器:扮演容器与算法之间的胶合剂,是所谓的“泛型指针”,共有五种类型,以及其它衍生变化。从实现的角度看,迭代器是一种将operator*、operator->、operator++、operator--等指针相关操作予以重载的类模板,所有STL容器都附带有自己专属的迭代器。
(4)仿函数:行为类似函数,可以作为算法的某种策略。从实现的角度看,仿函数是一种重载了operator()的类或者类模板。
(5)适配器:一种用来修饰容器或仿函数或迭代器接口的东西。例如stack和queue,虽然看似容器,但是只能算一种容器适配器,因为它们的底层完全借助deque,所有的操作都由底层的deque供应。
(6)分配器:负责空间配置和管理,从实现的角度讲,配置器是一个实现了动态空间配置、空间管理、空间释放的类模板。通常,分配器是由两级分配器构成的内存管理器,当申请的内存大于128B时,就启用第一级分配器通过malloc直接向系统的堆空间分配,如果申请的内存小于128B时,就启用第二级分配器,从一个预先定义好的内存池取一块内存交付给用户,这个内存池由16个不同大小(8~128B)空闲列表组成,分配器会根据申请内存的大小(将这个大小round up成8的倍数)从对应的空闲列表取表头块给用户。这种做法有两个优点:1)小对象快速分配,小对象从内存池分配;2)小对象从内存池分配,避免了内部碎片的产生。
六大组件关系:容器(containers)通过分配器(allocators)取得数据储存空间,算法(algorithms)通过迭代器(iterators)存取容器的内容,仿函数(functors)可以协助算法完成不同的策略变化,适配器(adapters)可以修饰或套接仿函数。
2、STL中的容器及底层实现:
(1)动态数组vector:底层数据结构为数组 ,支持快速随机访问;
(2)静态数组array:底层数据结构为数组 ,支持快速随机访问;
(3)双端队列deque:底层数据结构为一个中央控制器和多个缓冲区,支持首尾(中间不能)快速增删,也支持随机访问。deque是一个双端队列(double-ended queue),也是在堆中保存内容的.它的保存形式如下:[堆1] --> [堆2] -->[堆3] --> ...每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品。
(4)双向链表list:底层数据结构为双向链表,支持快速增删;
(5)单向链表forward_list:
(6)有序集合set:底层数据结构为红黑树,有序,不重复
(7)有序元素可重复集合multiset:底层数据结构为红黑树,有序,可重复
(8)有序字典map:底层数据结构为红黑树,有序,不重复
(9)有序元素可重复字典multimap:底层数据结构为红黑树,有序,可重复
(10)无序集合unordered_set:
(11)无序元素可重复集合unordered_multiset:
(12)无序字典unordered_map:
(13)无序元素可重复字典unordered_multimap:
(14)字符串string:
(15)栈stack:底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时;
(16)队列queue:底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时;
(17)优先级队列priority_queue:
(18)bit集合bitset:
3、常用的几个容器vector、set、map、stack、queue、string及其成员函数
(1)容器的公用函数:
vector<int> v1 = {x1,x2,...} | 第一种初始化方式 |
vector<int> v2{x1,x2,...} | 第二种初始化方式 |
list<int> l; vecto<int> v3(l.begin(), l.end()) |
第三种初始化方式,以另种容器的元素为初值 |
int carray[] = {x1,x2,...} set<int> c(begin(carray), end(carray)) |
第四种初始化方式,以某个C语言的array的元素作为初值 |
for(const auto& elem : coll){ elem } | 只读的方式访问元素,auto自定确定容器的元素类型 |
for(auto& elem : coll){ elem } | 可写的方式访问元素 |
for(auto pos = coll.cbegin(); pos!=coll.cend(); ++pos){ *pos } | 迭代器的方式只读访问元素 |
for(auto pos = coll.begin(); pos!=coll.end(); ++pos){ *pos } | 迭代器的方式可写访问元素 |
c.empty() | 判断容器是否为空,为空则返回true,否则返回false |
c.size() | 返回容器的元素数量,不适合forward_list<> |
c.begin() | 返回一个iterator,指向第一个元素 |
c.end() | 返回一个iterator,指向最末元素的下一个位置 |
c.cbegin() | 返回一个const iterator,指向第一个元素 |
c.cend() | 返回一个const iterator,指向最末元素的下一个位置 |
c.clear() | 移除所有元素,令容器为空,不适用于array<> |
(2)动态数组vector<>
vector将其元素放在一个动态数组中管理,它允许随机访问,在vector尾部附加或者移除元素都很快,但是在vector中间或起始段安插元素比较费时。
vector<int> v1 vector<int> v2 = v1 vector<int> v2(v1) vector<int> v2(n) vector<int> v2(n, elem) vector<int> v2(beg, end) vector<int> v2(initlist) vector<int> v2 = initlist. |
产生一个空的vector 使用v1初始化v2 使用v1初始化v2 构建一个大小为n的vector 构建一个大小为n的vector,每个元素为elem 使用iterator初始化v2 使用初始化列表初始化v2 使用初始化列表初始化v2 |
v.~vector() | 销毁所有元素,释放内存 |
v.capacity() | 返回不进行空间重新分配条件下的元素最大容纳量 |
v.reserve(num) | 如果容量不足,扩大容量 |
v.shrink_to_fit() | 降低容量以符合元素个数 |
v.assign(n, elem) | 复制n个elem,复制给v |
v.assign(beg, end) | 将区间内元素赋值给v |
v.assign(initlist) | 将初值列赋值给v |
v1.swap(v2) | 交换v1和v2的数据 |
swap(v1, v2) | 交换v1和v2的数据 |
v [idx] | 返回索引idx所指的元素 |
v.at( idx ) | 返回索引idx所指的元素 |
v.front() | 返回第一个元素 |
v.back() | 返回最后一个元素 |
v.rbegin() | 返回反向iterator指向的反向迭代的第一个元素 |
v.rend() | 返回反向iterator指向的反向迭代最后一个元素的下一个位置 |
v.crbegin() | 返回const iterator反向迭代的第一个元素 |
v.crend() | 返回const iterator反向迭代的最后一个元素的下一个位置 |
v.push_back(elem) | 附加一个elem到末尾 |
v.pop_back() | 移除最后一个元素,但是不返回它 |
v.insert(pos, elem) | 在iterator位置pos之前插入elem,并返回新元素位置 |
v.insert(pos, n, elem) | 在iterator位置pos之前插入n个elem,并返回第一个新元素位置 |
v.insert(pos, beg, end) | 在iterator位置pos之前插入区间内所有元素,并返回第一个新元素位置 |
v.insert(pos, initlist) | 在iterator位置pos之前插入initlist所有元素,并返回第一个新元素位置 |
v.emplace(pos, args...) | 在iterator位置pos之前插入一个以args为初值的元素,并返回新元素位置 |
v.emplace_back(args...) | 附加一个以args为初值的元素于末尾,不返回任何东西 |
v.erase(pos) | 移除iterator位置pos上的元素,返回下一个元素位置 |
v.erase(beg, end) | 移除区间内的所有元素,返回下一个元素位置 |
v.resize(num) | 将元素量改为num,多出的元素用default构造函数完成初始化 |
v.resize(num, elem) | 将元素量改为num,多出的元素用elem初始化 |
strcpy(&v[0], "hello world") strcpy(v.data(), "hello world") printf("%s\n", v.data()) |
把vector当做C语言中的数组使用 |
(3)有序集合set<>
set通常是由平衡二叉树实现出来(通常用红黑树实现),在二叉树中,每个节点都有一个父节点和两个子节点,左子树的所有元素都比自己小,右子树的所有元素都比自己大。set的主要优点是,它能很快找出一个具有某特定value的元素,因为它具备对数的复杂度,而对于循序式容器的复杂度是线性的。然而它的一个缺点是,你不能直接改变元素的value,因为那会破坏元素的自动排序。
set<int> s1 set<int> s1(op) set<int> s2 = s1 set<int> s2(s1) set<int> s2(beg, end) set<int> s2(beg, end, op) set<int> s2(initlist) set<int> s2 = initlist s2.~set() set<int> set<int, op> |
默认构造函数,建立一个空的set 建立一个空的set,以op作为排序规则 Copy构造函数,为相同类型的另一个set建立一份拷贝,所有元素均被复制 Copy构造函数,为相同类型的另一个set建立一份拷贝,所有元素均被复制 以区间内的元素作为初值,建立一个set 以区间内的元素为初值,以op为排序准则,建立一个set 以初值initlist的元素为初值建立一个set 以初值initlist的元素为初值建立一个set 销毁所有的元素,释放内存 一个set,以less<>(operator <)为排序准则 一个set,以op为排序准则 |
s.key_comp() | 返回“比较准则” |
s.value_comp() | 返回针对value的“比较准则”,和key_comp()相同 |
s.count(val) | 返回元素值为val 的元素个数 |
s.find(val) | 返回元素值为val 的第一个元素,如果找不到就返回end() |
s.lower_bound(val) | 返回val的第一个可安插位置,就是元素值>=val的第一个元素位置 |
s.upper_bound(val) | 返回val的最后一个可安插位置,就是元素值>val的第一个元素位置 |
s.equal_range(val) | 返回val可被安插的第一个位置和最后一个位置,就是元素值==val的元素区间 |
s1.swap(s2) | 交换s1和s2的数据 |
swap(s1, s2) | 交换s1和s2的数据 |
s.rbegin() | 返回反向iterator指向的反向迭代的第一个元素 |
s.rend() | 返回反向iterator指向的反向迭代最后一个元素的下一个位置 |
s.crbegin() | 返回const iterator反向迭代的第一个元素 |
s.crend() | 返回const iterator反向迭代的最后一个元素的下一个位置 |
s.insert(val) | 安插一个val拷贝,并返回新元素位置 |
s.insert(pos, val) | 安插val的拷贝,返回新元素位置,pos是个提示,指出安插动作的查找起点 |
s.insert(beg, end) | 将区间内所有元素拷贝到s,无返回值 |
s.insert(initlist) | 插入initlist所有元素,无返回值 |
s.emplace(args...) | 插入一个以args为初值的元素,并返回新元素位置 |
s.emplace_hind(pos, args...) | 插入一个以args为初值的元素,并返回新元素位置,pos是个提示,指出安插动作的查找起点 |
s.erase(val) | 移除与val相等的所有元素,返回被移除的元素个数 |
s.erase(pos) | 移除iterator位置pos上的元素,无返回值 |
s.erase(beg, end) | 移除区间内的所有元素,无返回值 |
(4)有序字典map<>
map和set类似,通常由平衡二叉树完成,可以把set看作特殊的map,只不过set的key和value是同一对象。因此,map拥有set的所有能力和所有操作。唯一的区别是map的元素key/value的键值对,此外map可以作为关联式数组来运用。map会根据key自动对元素排序,这一性质使得map身上有一条重要限制:你不可以直接改变元素key,因为这会破坏正确次序,要修改元素的key,必须先移除拥有该key的元素,然后插入拥有新key/value的元素。
map<int> m1 map<int> m1(op) map<int> m2 = m1 map<int> m2(m1) map<int> m2(beg, end) map<int> m2(beg, end, op) map<int> m2(initlist) map<int> m2 = initlist m2.~map() map<key, val> map<key, val, op> |
默认构造函数,建立一个空的map 建立一个空的map,以op作为排序规则 Copy构造函数,为相同类型的另一个map建立一份拷贝,所有元素均被复制 Copy构造函数,为相同类型的另一个map建立一份拷贝,所有元素均被复制 以区间内的元素作为初值,建立一个map 以区间内的元素为初值,以op为排序准则,建立一个map 以初值initlist的元素为初值建立一个map 以初值initlist的元素为初值建立一个map 销毁所有的元素,释放内存 一个map,以less<>(operator <)为排序准则 一个map,以op为排序准则 |
m[key] | 如果关键字为key的元素不存在,则安插关键字为key的元素,如果不设置value,value默认初始化为0。如果存在则返回value值。 |
m.at(key) | 返回key对应的value,如果该元素不存在则抛出异常 |
for(auto iter = m.begin();iter!=m.end();iter++){ cout<<iter->first<<endl; cout<<iter->second<<endl; } |
遍历map中的所有元素 |
m.key_comp() | 返回“比较准则” |
m.value_comp() | 返回针对value的“比较准则”,和key_comp()相同 |
m.count(val) | 返回key为val 的元素个数 |
m.find(val) | 返回key为val 的第一个元素,如果找不到就返回end() |
m.lower_bound(val) | 返回key=val的第一个可安插位置,就是key>=val的第一个元素位置 |
m.upper_bound(val) | 返回key=val的最后一个可安插位置,就是key>val的第一个元素位置 |
m.equal_range(val) | 返回key=val可被安插的第一个位置和最后一个位置,就是key==val的元素区间 |
m1.swap(m2) | 交换m1和m2的数据 |
swap(m1, m2) | 交换m1和m2的数据 |
m.rbegin() | 返回反向iterator指向的反向迭代的第一个元素 |
m.rend() | 返回反向iterator指向的反向迭代最后一个元素的下一个位置 |
m.crbegin() | 返回const iterator反向迭代的第一个元素 |
m.crend() | 返回const iterator反向迭代的最后一个元素的下一个位置 |
m.insert(val) | 安插一个val拷贝,并返回新元素位置 |
m.insert(pos, val) | 安插val的拷贝,返回新元素位置,pos是个提示,指出安插动作的查找起点 |
m.insert(beg, end) | 将区间内所有元素拷贝到s,无返回值 |
m.insert(initlist) | 插入initlist所有元素,无返回值 |
m.emplace(args...) | 插入一个以args为初值的元素,并返回新元素位置 |
m.emplace_hind(pos, args...) | 插入一个以args为初值的元素,并返回新元素位置,pos是个提示,指出安插动作的查找起点 |
m.erase(val) | 移除与val相等的所有元素,返回被移除的元素个数 |
m.erase(pos) | 移除iterator位置pos上的元素,无返回值 |
m.erase(beg, end) | 移除区间内的所有元素,无返回值 |
(5)字符串string
=、assign() | 赋予新值 |
swap() | 交换两个string的内容 |
+=、append()、push_back() | 添加字符。 str1.append(str2,1,3)表示将str2的从索引1开始的三个字符附加到str1; str1.append("nico",5)表示将添加字符串数组:'n' 'i' 'c' 'o' '\0'; str1.append(5, 'x') 表示添加5个x; |
insert(index,str) | 插入字符 |
erase()、pop_back() | 删除字符 str.erase(13):删除第13个字符 str.erase(7, 5):删除第七个位置后的5个字符 |
clear() | 移除全部字符使之为空 |
resize() | 改变字符数量 |
replace(index,num,str2) | 替换字符,将str中重index开始的num个字符替换为str2 |
+ | 串接string |
==,!=,<=,>=,compare() | 比较string,str1.compare(str2)中,返回0表示相等、小于0表示小于,大于0表示大于; str1.compare(0, 2,str2,2, 2)表示比较str1的从0开始的2个字符和str2的从2开始的两个字符; str1.compare(1,2,“bcx”,2)表示比较str1的从1开始的两个字符和“bcx”中的“bc”; |
empty() | 判断字符是否为空 |
size()、length() | 返回字符的数量 |
max_size() | 返回字符的最大可能数 |
capacity() |
返回重分配前的字符容量 |
reserve() | 保留一定容量内存以容纳一定数量的字符 |
shringk_to_fit() | 缩减字符使符合当前字符量 |
[]、at | 访问某个单一字符 |
front()、back() | 访问第一个、最末字符 |
>>,getline() | 从一个stream读取某值 |
<< | 将某值写入stream |
stoi()、stol()、stoll() | 将string转为带正负号的整数(int、long、long long),转换时会跳过前导的任何空白字符 stoi(str,idxRet,base=10):第二个参数用于保存字符中未被处理的第一个字符索引,也就是字符串中的第一个无效字符,如果不关心则设置nullptr,第三个参数是数值的基数。 |
stoul()、stoull() | 将string转为不带正负号的整数(unsigned int、unsigned long long) |
stof()、stod()、stold() | 将string转为浮点数(float、double、long double) |
to_string(val)、to_wstring(val) | 将整数、浮点数转为string |
const char c = 'a'; string s(1,c); --------------------- string s1; --------------------- s1.push_back(c); stringstream ss; --------------------- ss << c; string str2 = ss.str(); |
将char转为string的三种方法: 1.使用 string 的构造函数; 2.声明string 后将char push_back; 3.使用stringstream; |
copy(buffer,num,beg_index) | 将string复制为一个字符数组 |
data()、c_str() | 将string的内容以C-string的内容返回 |
substr() | 返回某个子字符串: str.substr(11):返回str的11个位置后的所有子串 str.substr(5, 6) :返回str的第5个位置后的6个字符 |
find() rfind() find_first_of() find_last_of() find_first_not_of() find_last_not_of() |
查找第一个与val相等的字符串,返回索引; str1.find(str2,10):表示从str1的第10个位置开始查找 查找最后一个与val相等的字符串,返回索引; 查找第一个与val中的某值相等的字符串; 查找最后一个与val中的某值相等的字符串; 查找第一个与val中的任何值都不相等的字符串; 查找最后一个与val中的任何值都不相等的字符串; |
begin()、end() | 提供正常的iterator支持 |
cbegin()、cend() | 提供const iterator支持 |
rbegin()、rend() | 提供reverse iterator支持 |
crbegin()、crend() | 提供const reverse iterator支持 |
get_allocator() | 返回分配器 |
(6)栈stack<>
stack<string> stack | 初始化一个空的栈 |
stack.push(val) | 将一个元素放入stack之内 |
stack.top() | 返回stack的下一个元素,但并不移除它 |
stack.pop() | 从stack中移除元素,但并不返回它 |
(7)队列queue<>
queue<string> queue | 初始化一个空队列 |
queue.push(val) | 将一个元素放入队列内 |
queue.front() | 返回队列的下一个元素(也就是第一个被插入的元素),但不移除它 |
queue.back() | 返回队列的最后一个元素(也就是最后一个被插入的元素),但不移除它 |
queue.pop() | 从队列中移除下一个元素,但并不返回它 |