C++ STL c语言中文网的STL教程
stl 是一个将算法与底层抽象实现相分离的库,通过它我们可以很轻松地,高效地实现很多复杂的算法。但是学习stl不仅仅要学习其使用方式,更重要的是学习其底层实现,若是掌握了其底层实现逻辑,则不仅拥有了扎实的数据结构的基本功,更是在c++修仙之路上修炼了一本武林秘籍。
c++ stl 的头文件有:
1 2 3 4 5 6 7 8 9 10 11 12 13 <iterator> <functional> <vector> <deque> <list> <queue> <stack> <set> <map> <algorithm> <numeric> <memory> <utility>
stl提供三种标准容器,分别为序列容器,排序容器,哈希容器。后两种也称为关联容器。
容器种类
功能
序列容器
主要包括vector,list,deque。元素在容器中的位置和元素的值没有关系,容器是非排序的。将元素插入时,指定在什么位置,元素就会在什么位置
排序容器
包含set,multiset,map,multimap.元素默认是从大到小排好的,插入元素时元素会自动插入到适当的位置。关联容器在查找时有着很好的性能。
哈希容器
unordered_set,unordered_map,unordered_multiset,unordered_multimap。和排序容器不同,哈希容器中元素的位置是未排序的,元素的位置有哈希函数确定。
迭代器
stl标准库为每一种容器定义了一种迭代器,不同的容器迭代器不同,其功能强弱也不同。
前向迭代器(forward iterator) 假设p是一个前向迭代器,则p支持p++,++p,*p操作,可以用相互赋值,可以使用==与!=进行比较。
双向迭代器(bidirectional iterator) 相比于前向迭代器,可以进行–p和p–操作。
随机访问迭代器(random acccess iterator) 除了双向迭代器的所有操作还支持以下操作:
p+=i
p-=i
p+i
p-i
p[i]
不同容器的迭代器
容器
迭代器
array
随机
vector
随机
deque
随机
list
双向
set/multiset
双向
map/multimap
双向
forward_list
前向
unordered_set/unordered_multiset
前向
unordered_map/unordered_multimap
前向
stack
不支持
queue
不支持
迭代器的定义方式
迭代器类型
具体格式
正向迭代器
容器类名::iterator 迭代器名
常量正向迭代器
容器类名::const_iterator 迭代器名;
反向迭代器
容器类名::reverse_iterator 迭代器名
常量反向迭代器
容器类名::const_reverse_iterator 迭代器名
序列式容器 array 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <iostream> #include <array> using namespace std;int main () { array<int , 4> myarray{}; for (int i = 0 ; i < myarray.size (); i++) { myarray.at (i) = i; } cout << get<3 >(myarray) << endl; cout << myarray[3 ] << endl; array<int , 4>::iterator it; if (!myarray.empty ()) for (it = myarray.begin (); it <= myarray.end () - 1 ; it++) cout << *it << " " ; cout << endl; array<int , 4>::reverse_iterator it_r; if (!myarray.empty ()) for (it_r = myarray.rend () - 1 ; it_r >= myarray.rbegin (); it_r--) cout << *it_r << " " ; cout << endl; int index = 1 ; cout << myarray[index] << " " << myarray[index + 1 ] << endl; cout << myarray.front () << endl; cout << myarray.back () << endl; int *p = myarray.data (); cout << *p << endl; myarray.fill (9 ); if (!myarray.empty ()) for (it = myarray.begin (); it <= myarray.end () - 1 ; it++) cout << *it << " " ; cout << endl; array<int , 10> array1; array1.fill (1 ); array<int , 10> array2; array2.fill (2 ); array<int , 10>::iterator it_10; if (!array1.empty ()) for (it_10 = array1.begin (); it_10 <= array1.end () - 1 ; it_10++) cout << *it_10 << " " ; cout << endl; if (!array2.empty ()) for (it_10 = array2.begin (); it_10 <= array2.end () - 1 ; it_10++) cout << *it_10 << " " ; cout << endl; array1.swap (array2); if (!array1.empty ()) for (it_10 = array1.begin (); it_10 <= array1.end () - 1 ; it_10++) cout << *it_10 << " " ; cout << endl; if (!array2.empty ()) for (it_10 = array2.begin (); it_10 <= array2.end () - 1 ; it_10++) cout << *it_10 << " " ; cout << endl; }
vector vector 容器是 STL 中最常用的容器之一,它和 array 容器非常类似,都可以看做是对 C++ 普通数组的“升级版”。不同之处在于,array 实现的是静态数组(容量固定的数组),而 vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会动态调整所占用的内存空间,整个过程无需人工干预。vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)。
还需注意的是,如果调用 reserve() 来增加容器容量,之前创建好的任何迭代器(例如开始迭代器和结束迭代器)都可能会失效,这是因为,为了增加容器的容量,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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 #include <iostream> #include <array> #include <vector> using namespace std;int main () { vector<double > v0{1.0 , 2.0 , 3.0 }; cout << v0.capacity () << endl; v0.reserve (100 ); cout << v0.capacity () << endl; v0.shrink_to_fit (); cout << v0.capacity () << endl; cout << v0.size () << endl; vector<double >::iterator it1; if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; vector<double > v1 (100 ) ; if (!v0.empty ()) for (it1 = v1.begin (); it1 <= v1.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; vector<double > v2 (100 , 10 ) ; if (!v0.empty ()) for (it1 = v2.begin (); it1 <= v2.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; vector<double > v3 (v2) ; if (!v0.empty ()) for (it1 = v3.begin (); it1 <= v3.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; vector<double > v4 (v2.data(), v2.data() + 1 ) ; if (!v0.empty ()) for (it1 = v4.begin (); it1 <= v4.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; cout << v0[0 ] << endl; cout << v0.at (0 ) << endl; cout << v0.front () << endl; cout << v0.back () << endl; v0.assign (v1.data (), v1.data () + 5 ); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; v0.push_back (1 ); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; v0.pop_back (); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v0.begin (); cout << *it1 << endl; it1 = v0.insert (it1, 1 ); cout << *it1 << endl; if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v0.begin (); v0.insert (it1, 2 , 4 ); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v0.begin (); v0.insert (it1, v0.data (), v0.data () + 7 ); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v0.begin (); v0.erase (it1); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v0.begin (); while (it1 != v0.end ()) v0.erase (it1); if (v0.empty ()) cout << "空了" << endl; for (int i = 0 ; i <= 10 ; i++) v0.push_back (i); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v0.begin (); v0.erase (it1, it1 + 2 ); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; int re_size = v0.size (); it1 = v0.begin (); for (int i = 0 ; i <= re_size; i++) cout << *(it1 + i) << endl; cout << v0.capacity () << endl; cout << v0.size () << endl; v0.clear (); cout << v0.capacity () << endl; cout << v0.size () << endl; if (v0.empty ()) cout << "空了" << endl; v0.reserve (100 ); it1 = v0.begin (); for (int i = 0 ; i <= re_size; i++) cout << *(it1 + i) << endl; v0.clear (); v1.clear (); v0.push_back (0 ); v1.push_back (1 ); it1 = v0.begin (); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v1.begin (); if (!v1.empty ()) for (it1 = v1.begin (); it1 <= v1.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; v0.swap (v1); it1 = v0.begin (); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v1.begin (); if (!v1.empty ()) for (it1 = v1.begin (); it1 <= v1.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; it1 = v0.begin (); v0.emplace (it1, 2 ); it1 = v0.begin (); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; v0.emplace_back (3 ); it1 = v0.begin (); if (!v0.empty ()) for (it1 = v0.begin (); it1 <= v0.end () - 1 ; it1++) cout << *it1 << " " ; cout << endl; }
deque 双队列容器 和 vector 相比,额外增加了实现在容器头部添加和删除元素的成员函数,同时删除了 capacity()、reserve() 和 data() 成员函数。
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 #include <iostream> #include <array> #include <vector> #include <deque> using namespace std;int main () { deque<int > dq{0 , 1 , 2 , 3 , 4 , 5 }; deque<int >::iterator it; it = dq.begin (); if (!dq.empty ()) for (it = dq.begin (); it <= dq.end () - 1 ; it++) cout << *it; cout << endl; dq.push_front (1 ); it = dq.begin (); if (!dq.empty ()) for (it = dq.begin (); it <= dq.end () - 1 ; it++) cout << *it; cout << endl; dq.pop_front (); it = dq.begin (); if (!dq.empty ()) for (it = dq.begin (); it <= dq.end () - 1 ; it++) cout << *it; cout << endl; }
list 双链表容器 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 #include <iostream> #include <array> #include <vector> #include <deque> #include <list> using namespace std;bool demo (int a, int b) { return (a <= b); } int main () { list<int > l (10 , 0 ) ; list<int >::iterator it; it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it; cout << endl; l.remove (0 ); if (l.empty ()) cout << "你把0都删除了,我空了" << endl; list<int > l2 (5 , 2 ) ; l.splice (l.begin (), l2); it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it; cout << endl; l = {1 , 1 , 2 , 2 , 3 , 4 , 5 , 6 , 6 , 7 , 7 , 9 , 10 , 10 , 1 }; it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it; cout << endl; l.unique (); it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it; cout << endl; l.unique (demo); it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it; cout << endl; l.sort (); l.unique (); it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it; cout << endl; l2 = {1 , 4 , 2 , 31 , 54 , 234 , 2 , 523 , 42 , 42 , 10 }; l.merge (l2); it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it << " " ; cout << endl; l.sort (); l2.sort (); l.merge (l2); l.unique (); it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it << " " ; cout << endl; l.reverse (); it = l.begin (); if (!l.empty ()) for (it = l.begin (); it != l.end (); it++) cout << *it << " " ; cout << endl; }
forward_list 其实就是单链表,相比于双向链表list,forward_list 更加有效率,但是不能进行逆向遍历的操作。当一个操作list与forward_list都可以做的时候,应该优先选用forward_list.
关联式容器 关联式容器再存储元素的时候还会给每一个元素配备一个键,整体以键值相配对的方式进行存储,可以通过键值直接索引到元素无需遍历整个容器,再存储元素的时候会默认按照各个元素的大小做升序排列。关联式容器在更删改查的效率更高。关联式容器在底层使用红黑树进行实现。
关联式容器包括:
1 2 3 4 map multimap set multiset
容器
特点
map
元素的键是唯一的,根据各元素键的大小进行升序排序
set
元素的键是唯一的,根据各元素键的大小进行升序排序
multimap
和map的区别在于键可以重复
multiset
和set的区别在于键可以重复
map map容器存储的都是pair对象。
pair<const K, T>
键是不可以修改的。值是可以修改的。
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <map> #include <iostream> using namespace std;int main () { map<int , string> mymap{{3 , "三" }, {4 , "四" }}; mymap[1 ] = "一" ; mymap[2 ] = "二" ; map<int , string>::iterator it; it = mymap.begin (); if (!mymap.empty ()) for (it = mymap.begin (); it != mymap.end (); it++) cout << it->first << " " << it->second << endl; mymap[1 ] = "新一" ; it = mymap.begin (); if (!mymap.empty ()) for (it = mymap.begin (); it != mymap.end (); it++) cout << it->first << " " << it->second << endl; it = mymap.find (1 ); cout << it->first << " " << it->second << endl; it = mymap.lower_bound (2 ); cout << it->first << " " << it->second << endl; it = mymap.upper_bound (2 ); cout << it->first << " " << it->second << endl; pair<map<int , string>::iterator, map<int , string>::iterator> pair; pair = mymap.equal_range (2 ); cout << "范围low: " << pair.first->first << " " << pair.first->second << endl; cout << "范围high: " << pair.second->first << " " << pair.second->second << endl; cout << mymap[8 ] << endl; cout << mymap[1 ] << endl; cout << mymap.at (2 ) << endl; if (!mymap.at (8 ).empty ()) cout << mymap.at (8 ) << endl; mymap.insert ({5 , "五" }); mymap.insert ({5 , "肆虐五" }); it = mymap.begin (); if (!mymap.empty ()) for (it = mymap.begin (); it != mymap.end (); it++) cout << it->first << " " << it->second << endl; mymap.erase (8 ); it = mymap.begin (); if (!mymap.empty ()) for (it = mymap.begin (); it != mymap.end (); it++) cout << it->first << " " << it->second << endl; cout << mymap.count (1 ) << endl; }
multimap multimap 比 map 除了可以拥有重复的额值之外,没有什么不同的。略过。
set 和 map、multimap 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。
其余和map没啥不同,set就是集合,所以也不可以重复。
multiset multiset 比 set 除了可以拥有重复的额值之外,没有什么不同的。略过。
无序关联式容器 无序关联式容器,又称哈希容器。和关联式容器一样,此类容器存储的也是键值对元素;不同之处在于,关联式容器默认情况下会对存储的元素做升序排序,而无序关联式容器不会。和其它类容器相比,无序关联式容器擅长通过指定键查找对应的值,而遍历容器中存储元素的效率不如关联式容器。
关联式容器的底层实现采用的树存储结构,更确切的说是红黑树结构; 无序容器的底层实现采用的是哈希表的存储结构。
无序容器内部存储的键值对是无序的,各键值对的存储位置取决于该键值对中的键, 和关联式容器相比,无序容器擅长通过指定键查找对应的值(平均时间复杂度为 O(1));但对于使用迭代器遍历容器中存储的元素,无序容器的执行效率则不如关联式容器。
包含有 4 个具体容器,分别为 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset。
c++ 的常用算法