什么是STL STL称为标准模板库(Standard Template Library),是惠普实验室开发的一系列软件的统称。现主要出现在C++中,STL从广义上分为:容器(Container)、算法(Algorithm)和迭代器(Iterator)。STL几乎所有的代码都采用了模板类或者模板函数 ,这相比传统的由函数和类组成的库来说提供了更好的代码重用机会。
STL六大组件 STL提供了六大组件,彼此之间可以组合套用,这六大组件分别是容器、算法、迭代器、仿函数、适配器、空间配置器。其中,在算法竞赛中用到最多的为容器、算法与迭代器 。
容器(Container):STL容器为各种数据结构 ,如vector、stack、queue、map、set等,用来存放数据,从实现角度来看,STL容器是一种class template。
算法(Algorithm):STL的算法多数定义在 < algorithm > 头文件中,其中包括了各种常用的算法,如sort、find、copy、reverse等,从实现角度来看,STL算法是一种function template。
迭代器(Iterator):STL迭代器扮演了容器与算法之间的胶合剂,共有五种类型,从实现角度来看,迭代器是一种将opetator*、opetator->、operator++等指针相关操作予以重载的class template。所有STL容器都附带有自己专属的迭代器,只有容器的设计者才知道如何遍历自己的元素。
仿函数(Functor):行为类似函数,可作为算法的某种策略,从实现角度来看,仿函数是一种重载了operator()的class或者class template。
适配器(Adaptor):一种用来修饰容器或仿函数或迭代器接口的东西。
空间配置器(Allocator):负责空间的配置与管理。从实现角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的class template。
STL容器 1.vector vector又称变长数组 ,定义在 < vector > 头文件中,vector容器是动态空间 ,随着元素的加入,它的内部机制会自动扩充空间以容纳新的元素。因此vector的运用对于内存的合理利用与运用的灵活性有很大的帮助。
1.1 vector的定义方式 1 2 3 4 5 6 vector<int > v; vector<int > v[N]; vector<int > v (len) ; vector<int > v (len, x) ; vector<int > v2 (v1) ; vector<int > v2 (v1. begin(), v1. begin() + 3 ) ;
1.2 vector的常用内置函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 vector<int > v = { 1 , 2 , 3 }; vector<int >::iterator it = v.begin (); v.push_back (4 ); v.pop_back (); lower_bound (v.begin (), v.end (), 2 ); upper_bound (v.begin (), v.end (), 2 ); v.size (); v.empty (); v.front (); v.back (); v.begin (); v.end (); v.clear (); v.erase (v.begin ()); v.erase (v.begin (), v.begin () + 2 ); v.insert (v.begin (), 1 ); for (int i = 0 ; i < v.size (); i++) cout << v[i] << ' ' ;for (vector<int >::iterator it = v.begin (); it != v.end (); it++) cout << *it << ' ' ;for (auto x : v) cout << x << ' ' ;
2.stack stack又称栈 ,是一种后进先出 (Last In First Out,LIFO)的数据结构,定义在 < stack > 头文件中,stack容器允许新增元素、移除元素、取得栈顶元素,但是除了最顶端以外,没有任何方法可以存取stack的其它元素,换言之,stack不允许有遍历行为 。
2.1 stack的定义方式 1 2 stack<int > stk; stack<int > stk[N];
2.2 stack的常用内置函数 1 2 3 4 5 6 stack<int > stk; stk.push (x); stk.pop (); stk.top (); stk.size (); stk.empty ();
3 string string又称字符串 ,定义在 < string > 头文件中。C风格的字符串(以空字符结尾的字符数组)太过复杂难于掌握,因此C++标准库定义了一种string类。string和vector < char > 在数据结构、内存管理等方面都是相同的。但是,vector < char > 只是单纯的一个“char元素的容器”,而string不仅是一个“char元素的容器”,它还扩展了一些针对字符串的操作,例如string可以使用c_str()函数转换为C风格的字符串,vector中并未对输入输出流操作符进行重载,因此无法直接对vector < char > 进行cin或者cout这样的操作,但是string可以,且vector < char > 并不能直接实现字符串的拼接,但是string可以,string中重载了+, +=运算符。
3.1 string的定义方式 1 2 3 4 string str; string str[N]; string str (5 , 'a' ) ; string str ("abc" ) ;
3.2 string的常用内置函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 string str ("abcabc" ) ;str.push_back ('d' ); str.pop_back (); str.length (); str.size (); str.empty (); str.substr (1 ); str.substr (0 , 2 ); str.insert (1 , 2 , 'x' ); str.insert (1 , "yy" ); str.erase (1 , 4 ); str.find ('b' ); str.find ('b' , 2 ); str.find ("bc" ); str.find ("bc" , 2 ); str.rfind ('b' ); str.rfind ('b' , 3 ); str.rfind ("bc" ); str.rfind ("bc" , 3 ); stoi (str); to_string (value); str[0 ]; cout << (str == str) << endl;
3.3 string的erase( )与remove( )函数的用法 1 2 3 4 5 6 7 string str1, str2, str3, str4, str5; str1 = str2 = str3 = str4 = str5 = "I love AcWing! It's very funny!" ; str1. erase (15 ); str2. erase (6 , 11 ); str3. erase (str3. begin () + 2 ); str4. erase (str4. begin () + 7 , str4. end () - 11 ); str5. erase (remove (str5. begin (), str5. end (), 'n' ), str5. end ());
4 queue/priority_queue queue又称队列 ,是一种先进先出 (First In First Out,FIFO)的数据结构,定义在 < queue > 头文件中,queue容器允许从一端(称为队尾 )新增元素(入队),从另一端(称为队头 )移除元素(出队)。
priority_queue又称优先队列 ,同样定义在 < queue > 头文件中,与queue不同的地方在于我们可以自定义其中数据的优先级,优先级高的排在队列前面,优先出队。priority_queue具有queue的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它的本质是用堆 实现的,因此可分为小根堆 与大根堆 ,小根堆 中较小的元素排在前面,大根堆 中较大的元素排在前面。(创建priority_queue时默认是大根堆! )
4.1 queue/priority_queue的定义方式 1 2 3 4 5 queue<int > que; queue<int > que[N]; priority_queue<int > bigHeap; priority_queue<int , vector<int >, less<int > > bigHeap; priority_queue<int , vector<int >, greater<int > > smallHeap;
4.2 queue/priority_queue的常用内置函数 1 2 3 4 5 6 7 8 queue<int > que;priority_queue<int > bigHeap; que.push (x); que.pop (); que.front (); que.back (); que.size (); que.empty (); bigHeap.top ();
5 deque deque又称双端队列 ,定义在 < deque > 头文件中,vector容器是单向开口的连续内存空间,deque则是一种双向开口的连续线性空间 。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector也可以在头尾两端插入元素,但是在其头部进行插入操作效率很低。deque和vector最大的差异一是在于deque允许使用常数项时间在头部进行元素的插入和删除操作,二是在于deque没有容量的概念,因为它是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。
5.1 deque的定义方式 1 2 3 4 5 6 deque<int > deq; deque<int > deq[N]; deque<int > deq (len) ; deque<int > deq (len, x) ; deque<int > deq2 (deq1) ; deque<int > deq2 (deq1. begin(), deq1. begin() + 3 ) ;
5.2 deque的常用内置函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 deque<int > deq = { 1 , 2 , 3 }; deque<int >::iterator it = deq.begin (); deq.push_back (4 ); deq.pop_back (); deq.push_front (4 ); deq.pop_front (); deq.size (); deq.empty (); deq.front (); deq.back (); deq.begin (); deq.end (); deq.clear (); deq.erase (deq.begin ()); deq.erase (deq.begin (), deq.begin () + 2 ); deq.insert (deq.begin (), 1 ); for (int i = 0 ; i < deq.size (); i++) cout << deq[i] << ' ' ;for (deque<int >::iterator it = deq.begin (); it != deq.end (); it++) cout << *it << ' ' ;for (auto x : deq) cout << x << ' ' ;
6 map/multimap map/multimap又称映射 ,定义在 < map > 头文件中,map和multimap的底层实现机制都是红黑树。map的功能是能够将任意类型的元素映射到另一个任意类型的元素上 ,并且所有的元素都会根据元素的键值自动排序。map所有的元素都是pair,同时拥有键值 和实值 (即(key, value)对),key被视为键值 ,value被视为实值 ,map不允许两个元素有相同的键值。multimap和map的操作类似,唯一区别是multimap的键值允许重复。
6.1 map/multimap的定义方式 1 2 3 4 map<string, int > mp; map<string, int > mp[N]; multimap<string, int > mulmp; multimap<string, int > mulmp[N];
6.2 map/multimap的常用内置函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 map<string, int > mp;mp["abc" ] = 3 ; mp["ab" ]++; mp.insert (make_pair ("cd" , 2 )); mp.insert ({ "ef" , 5 }); mp.size (); mp.empty (); mp.clear (); mp.erase ("ef" ); mp["abc" ]; mp.begin (); mp.end (); mp.find ("ab" ); mp.find ({ "abc" , 3 }); mp.count ("abc" ); mp.count ({ "abc" , 2 }); mp.lower_bound ("abc" ); mp.upper_bound ("abc" ); for (map<string, int >::iterator it = mp.begin (); it != mp.end (); it++) cout << (*it).first << ' ' << (*it).second << endl;for (auto x : mp) cout << x.first << ' ' << x.second << endl;for (auto &[k, v] : mp) cout << k << ' ' << v << endl;
7 set/multiset set/multiset又称集合 ,定义在 < set > 头文件中。set的特性是所有元素都会根据元素的键值自动被排序,set的元素不像map那样可以同时拥有键值和实值,set的元素既是键值又是实值,set不允许两个元素有相同的键值,因此总结来说就是set中的元素是有序且不重复的 。multiset的特性和用法和set完全相同,唯一的区别在于multiset允许有重复元素,set和multiset的底层实现都是红黑树。
7.1 set/multiset的定义方式 1 2 3 4 set<int > st; set<int > st[N]; multiset<int > mulst; multiset<int > mulst[N];
7.2 set/multiset的常用内置函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 set<int > st; st.insert (5 ); st.insert (6 ); st.insert (7 ); st.size (); st.empty (); st.erase (6 ); st.begin (); st.end (); st.clear (); st.find (5 ); st.count (5 ); st.lower_bound (5 ); st.upper_bound (5 ); for (set<int >::iterator it = st.begin (); it != st.end (); it++) cout << (*it) << ' ' ;for (auto x : st) cout << x << ' ' ;
8 unordered_map/unordered_set unordered_map/unordered_set分别定义在 < unordered_map > 与 < unordered_set > 头文件中,内部采用的是hash表结构,拥有快速检索的功能。与map/set相比最大的区别在于unordered_map/unordered_set中的元素是无序 的,增删改查的时间复杂度为$O(1)O(1)O(1)$(map/set增删改查的时间复杂度为$O(logn)O(logn)O(logn)$),但是不支持lower_bound( )/upper_bound( )函数。
8.1 unordered_map/unordered_set的定义方式 1 2 3 4 unordered_set<int > st; unordered_set<int > st[N]; unordered_map<int , int > mp; unordered_map<int , int > mp[N];
8.2 unordered_map/unordered_set的常用内置函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 unordered_set<int > st; unordered_map<int , int > mp; st.insert (5 ); st.insert (6 ); st.insert (7 ); st.size (); st.empty (); st.erase (6 ); st.find (5 ); st.count (5 ); st.begin (); st.end (); st.clear (); mp.insert (make_pair (1 , 2 )); mp.insert ({ 3 , 4 }); mp.size (); mp.empty (); mp.erase (3 ); mp.find (1 ); mp.count (1 ); mp.begin (); mp.end (); mp.clear (); for (unordered_set<int >::iterator it = st.begin (); it != st.end (); it++) cout << (*it) << ' ' ;for (auto x : st) cout << x << ' ' ;for (unordered_map<int , int >::iterator it = mp.begin (); it != mp.end (); it++) cout << (*it).first << ' ' << (*it).second << endl;for (auto x : mp) cout << x.first << ' ' << x.second << endl;for (auto &[k, v] : mp) cout << k << ' ' << v << endl;
STL算法 C++标准库定义了一组泛型算法 ,之所以称为泛型指的是它们可以操作在多种容器上,不但可以作用于标准库类型,还可以用在内置数组类型甚至其它类型的序列上 。泛型算法定义在 < algorithm > 头文件中,标准库还定义了一组泛化的算术算法 (Generalized Numeric Algorithm),定义在 < numeric > 头文件中。使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <iostream> #include <algorithm> #include <numeric> using namespace std;int main () { int a[5 ] = { 1 , 2 , 3 , 4 , 5 }; int b[5 ] = { 0 }; sort (a, a + 5 ); sort (a, a + 5 , greater <int >()); reverse (a, a + 5 ); nth_element (a, a + 3 , a + 5 ); find (a, a + 5 , 3 ); binary_search (a, a + 5 , 2 ); count (a, a + 5 , 3 ); copy (a, a + 2 , a + 3 ); replace (a, a + 5 , 3 , 4 ); fill (a, a + 5 , 1 ); unique (a, a + 5 ); remove (a, a + 5 , 3 ); next_permutation (a, a + 5 ); prev_permutation (a, a + 5 ); partial_sum (a, a + 5 , b); adjacent_difference (a, a + 5 , b); adjacent_difference (a, a + 5 , b, plus <int >()); adjacent_difference (a, a + 5 , b, multiplies <int >()); return 0 ; }