学习笔记CPP程序设计C++ 标准模板库(Standard Template Library,STL)
小嗷犬C++ 标准模板库简介
C++ 标准模板库(Standard Template Library)是 C++ 标准库的一部分,不需要另外安装,直接导入即可使用。
STL 为程序员提供了通用的模板类,这些模板类可以用来实现各种数据结构和算法,从而使程序员不必从头开始编写这些代码。
STL 很好地实现了数据结构和算法的分离,大大降低了模块之间的耦合度,程序员可以自由组合 STL 提供的数据结构和算法。
STL 的核心由以下三个部分组成:
- 容器(Containers):各种数据结构,如
vector
、deque
、map
、set
等。 - 算法(Algorithms):各种算法,如排序、查找、转换等。
- 迭代器(Iterators):用于遍历容器的对象。
下面我们将从这三个部分来介绍 STL。
容器(Containers)
容器是用来存储数据的对象,它们提供了一种高效的方式来存储和访问数据。对于不同的任务,我们可以选择不同的容器来存储数据。
STL 的容器分为三类:
- 序列容器:序列容器会维护插入元素的顺序。常用的序列容器有
vector
、deque
。 - 关联容器:关联容器既有有序,也有无序,存储内容为键值对。常用的关联容器有
map
、set
。 - 容器适配器:容器适配器是序列容器或关联容器的变体,它们对接口做了更多的限制,并且不支持迭代器。常用的容器适配器有
queue
、priority_queue
、stack
。
序列容器
序列容器是一种线性的数据结构,它们会维护插入元素的顺序。本节我们将介绍 vector
、deque
这两种序列容器。
vector
vector
是一个动态数组,它的大小可以动态改变。它可以随机访问、连续存储,长度也非常灵活。
由于它优良的性质,vector
成为了程序设计中首选的序列容器。
在你没有更好的理由选择其他容器的情况下,你应该使用 vector
!
vector
以模板类的形式定义在头文件 <vector>
中,并位于 std
命名空间中,因此使用 vector
时需要包含头文件并使用 std
命名空间:
1 2
| #include <vector> using namespace std;
|
vector 的定义
vector
和 C++ 中基本数据类型的定义类似,只需要在 vector
后面加上尖括号 <>
,并在尖括号中指定存储的数据类型即可。
类型名可以是任意的 C++ 基本数据类型,如 int
、double
等,也可以是 STL 中的容器,如 vector
、map
等,甚至可以是自定义的结构体或是自定义的类。
1 2 3 4 5 6
| vector<int> v1; vector<double> v2; vector<vector<int> > v3;
vector<int> v4[10];
|
vector 定义时还可以指定初始元素的个数和初始值。
1 2 3 4
| vector<int> v1(10); vector<int> v2(10, 1); vector<int> v3{1, 2, 3}; vector<int> v4 = {1, 2, 3};
|
vector 的常用方法
下表列出了 vector
的一些常用方法:
方法 | 说明 |
---|
v.empty() | 判断 v 是否为空 |
v.size() | 返回 v 的大小 |
v.push_back(x) | 在 v 的末尾添加一个元素 x |
v.clear() | 删除 v 中的所有元素 |
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> #include <vector> using namespace std;
int main() { vector<int> v; cout << v.empty() << endl; cout << v.size() << endl;
v.push_back(1); v.push_back(2); v.push_back(3); cout << v.empty() << endl; cout << v.size() << endl;
v.clear(); cout << v.empty() << endl; cout << v.size() << endl;
return 0; }
|
vector 的遍历
vector
的遍历与数组的遍历类似,可以使用下标来访问各元素的值,也可以使用迭代器来遍历,C++11 中还可以使用范围 for 循环。
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
| #include <iostream> #include <vector> using namespace std;
int main() { vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3);
for (int i = 0; i < v.size(); i++) { cout << v[i] << ' '; } cout << endl;
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << *it << ' '; } cout << endl;
for (int x : v) { cout << x << ' '; } cout << endl;
return 0; }
|
除此之外,下标还可以用来随机访问和修改 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
| #include <iostream> #include <vector> using namespace std;
int main() { vector<int> v{1, 2, 3};
cout << v[0] << endl; cout << v[1] << endl; cout << v[2] << endl;
v[0] = 4; v[1] = 5; v[2] = 6;
for (int x : v) { cout << x << ' '; } cout << endl;
return 0; }
|
deque
deque
是一个双端队列,它的大小可以动态改变。它可以随机访问、连续存储,长度也非常灵活。
deque
和 vector
的区别在于,deque
可以在头部和尾部快速插入和删除元素,而 vector
只能在尾部快速插入和删除元素。
deque
以模板类的形式定义在头文件 <deque>
中,并位于 std
命名空间中,因此使用 deque
时需要包含头文件并使用 std
命名空间:
1 2
| #include <deque> using namespace std;
|
deque 的定义
deque
的定义与 vector
类似,只需要在 deque
后面加上尖括号 <>
,并在尖括号中指定存储的数据类型即可。
类型名可以是任意的 C++ 基本数据类型,如 int
、double
等,也可以是 STL 中的容器,如 vector
、map
等,甚至可以是自定义的结构体或是自定义的类。
1 2 3 4 5 6
| deque<int> d1; deque<double> d2; deque<deque<int> > d3;
deque<int> d4[10];
|
deque
定义时还可以指定初始元素的个数和初始值。
1 2 3 4
| deque<int> d1(10); deque<int> d2(10, 1); deque<int> d3{1, 2, 3}; deque<int> d4 = {1, 2, 3};
|
deque 的常用方法
下表列出了 deque
的一些常用方法:
方法 | 说明 |
---|
d.empty() | 判断 d 是否为空 |
d.size() | 返回 d 的大小 |
d.push_back(x) | 在 d 的末尾添加一个元素 x |
d.push_front(x) | 在 d 的头部添加一个元素 x |
d.pop_back() | 删除 d 的末尾元素 |
d.pop_front() | 删除 d 的头部元素 |
d.clear() | 删除 d 中的所有元素 |
示例代码如下:
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
| #include <iostream> #include <deque> using namespace std;
int main() { deque<int> d; cout << d.empty() << endl; cout << d.size() << endl;
d.push_back(1); d.push_back(2); d.push_back(3); cout << d.empty() << endl; cout << d.size() << endl;
d.push_front(4); d.push_front(5); d.push_front(6); cout << d.empty() << endl; cout << d.size() << endl;
d.pop_back(); d.pop_front(); cout << d.empty() << endl; cout << d.size() << endl;
d.clear(); cout << d.empty() << endl; cout << d.size() << endl;
return 0; }
|
deque 的遍历
deque
的遍历与 vector
类似,也可以使用下标、迭代器和范围 for 循环。
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
| #include <iostream> #include <deque> using namespace std;
int main() { deque<int> d{1, 2, 3};
for (int i = 0; i < d.size(); i++) { cout << d[i] << ' '; } cout << endl;
for (deque<int>::iterator it = d.begin(); it != d.end(); it++) { cout << *it << ' '; } cout << endl;
for (int x : d) { cout << x << ' '; } cout << endl;
return 0; }
|
除此之外,下标还可以用于修改 deque
中的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <deque> using namespace std;
int main() { deque<int> d{1, 2, 3};
d[0] = 4; d[1] = 5; d[2] = 6;
for (int x : d) { cout << x << ' '; } cout << endl;
return 0; }
|
关联容器
关联容器是一种用于存储一对对关联元素的容器,其中每对元素又称为一个键值对(key-value pair),关联容器中的元素是按照键值对的方式存储的,我们可以通过键来快速查找对应的值。本节将介绍 pair
类和 map
、set
这两种关联容器。
pair
pair
是一种用于存储一对元素的模板类,后续介绍的 map
和 set
存储的基本单元就是 pair
。
pair
以模板类的形式定义在头文件 <utility>
中,并位于 std
命名空间中,因此使用 pair
时需要包含头文件并使用 std
命名空间:
1 2
| #include <utility> using namespace std;
|
pair 的定义
pair
的定义格式如下:
类型名1和类型名2可以是任意的 C++ 内置类型或自定义类型:
1 2 3 4 5 6
| pair<int, int> p1; pair<string, int> p2; pair<string, pair<int, int> > p3;
pair<int, int> p4[10];
|
pair
定义时可以直接初始化:
1 2
| pair<int, int> p1(1, 2); pair<string, int> p2("hello", 1);
|
pair 的访问与修改
pair
中的元素可以通过 first
和 second
来访问和修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream> #include <utility> using namespace std;
int main() { pair<int, int> p(1, 2);
cout << p.first << ' ' << p.second << endl;
p.first = 3; p.second = 4; cout << p.first << ' ' << p.second << endl;
return 0; }
|
map
map
是一种用于存储键值对的关联容器,基于红黑树(一种平衡二叉树)实现,键值对中的键是唯一的,而值则可以重复。map
的默认排序规则是按照键的升序排序,我们可以通过 map
快速查找某个键对应的值。
map
以模板类的形式定义在头文件 <map>
中,并位于 std
命名空间中,因此使用 map
时需要包含头文件并使用 std
命名空间:
1 2
| #include <map> using namespace std;
|
map 的定义
map
的定义格式如下:
键类型名和值类型名可以是任意的 C++ 内置类型或自定义类型:
1 2 3 4
| map<int, int> m1; map<string, int> m2; map<string, string> m3; map<string, vector<int>> m4;
|
map
定义时还可以指定初始元素。
1 2
| map<int, int> m1{{1, 2}, {3, 4}, {5, 6}}; map<int, int> m2(m1);
|
map 的常用方法
下表列出了 map
的一些常用方法:
方法 | 说明 |
---|
m.empty() | 判断 m 是否为空 |
m.size() | 返回 m 的大小 |
m.insert(pair) | 向 m 中插入一个键值对 |
m.emplace(pair) | 在 m 中构造一个键值对,C++11 支持,效率高于 insert |
m.erase(key) | 删除 m 中键为 key 的键值对 |
m.clear() | 删除 m 中的所有键值对 |
m.count(key) | 返回 m 中键为 key 的键值对的个数 |
示例代码如下:
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
| #include <iostream> #include <map> using namespace std;
int main() { map<int, int> m; cout << m.empty() << endl; cout << m.size() << endl;
m.insert({1, 2}); m.insert({3, 4}); m.insert({5, 6}); cout << m.empty() << endl; cout << m.size() << endl;
m.erase(1); cout << m.empty() << endl; cout << m.size() << endl;
m.clear(); cout << m.empty() << endl; cout << m.size() << endl;
return 0; }
|
map 的遍历
map
的遍历可以使用迭代器和范围 for 循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <map> using namespace std;
int main() { map<int, int> m{{1, 2}, {3, 4}, {5, 6}};
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) { cout << it->first << ' ' << it->second << endl; }
for (pair<int, int> p : m) { cout << p.first << ' ' << p.second << endl; }
return 0; }
|
map 的查找
map
的查找可以使用 find
方法,该方法返回一个迭代器,指向键为 key
的键值对,如果 key
不存在,则返回 end
迭代器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> #include <map> using namespace std;
int main() { map<int, int> m{{1, 2}, {3, 4}, {5, 6}};
map<int, int>::iterator it = m.find(3); if (it != m.end()) { cout << it->first << ' ' << it->second << endl; }
return 0; }
|
我们更常用的是使用下标运算符 []
来查找或插入键值对,如果 key
不存在,则会自动插入一个键值对,其值为默认值。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> #include <map> using namespace std;
int main() { map<int, int> m{{1, 2}, {3, 4}, {5, 6}};
cout << m[3] << endl; cout << m[7] << endl; cout << m.size() << endl;
return 0; }
|
下标运算符 []
还可以用于修改键值对的值。
1 2 3 4 5 6 7 8 9 10 11 12
| #include <iostream> #include <map> using namespace std;
int main() { map<int, int> m{{1, 2}, {3, 4}, {5, 6}};
m[3] = 7; cout << m[3] << endl;
return 0; }
|
除此之外,map
还有一个无序的版本 unordered_map
,其定义和使用方法与 map
类似,只是 unordered_map
是基于哈希表实现的,因此查找和插入的时间复杂度为O(1),而 map
是基于红黑树实现的,因此查找和插入的时间复杂度为O(logn)。
set
set
是一个集合,其元素是唯一的,不允许重复,本质上是一个键值相等的 map
。
set
以模板类的形式定义在头文件 <set>
中,并位于 std
命名空间中,因此使用 set
时需要包含头文件并使用 std
命名空间:
1 2
| #include <set> using namespace std;
|
set
的定义如下:
类型名可以是任意的 C++ 内置类型或自定义类型:
1 2 3 4
| set<int> s1; set<string> s2; set<set<int> > s3;
|
set
定义时还可以指定初始元素。
1 2
| set<int> s1{1, 2, 3}; set<int> s2(s1);
|
set 的常用方法
下表列出了 set
的一些常用方法:
方法 | 说明 |
---|
s.empty() | 判断 s 是否为空 |
s.size() | 返回 s 的大小 |
s.insert(key) | 向 s 中插入元素 key |
s.emplace(key) | 在 s 中构造元素 key ,C++11 支持,效率高于 insert |
s.erase(key) | 删除 s 中的元素 key |
s.clear() | 清空 s |
s.count(key) | 返回 s 中元素 key 的个数,key 不存在时返回 0 |
示例代码如下:
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
| #include <iostream> #include <set> using namespace std;
int main() { set<int> s;
cout << s.empty() << endl; cout << s.size() << endl;
s.insert(1); s.insert(3); s.insert(5); cout << s.empty() << endl; cout << s.size() << endl;
s.erase(1); cout << s.empty() << endl; cout << s.size() << endl;
s.clear(); cout << s.empty() << endl; cout << s.size() << endl;
return 0; }
|
set 的遍历
set
的遍历可以使用迭代器或范围 for 循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <set> using namespace std;
int main() { set<int> s{1, 2, 3};
for (set<int>::iterator it = s.begin(); it != s.end(); it++) { cout << *it << endl; }
for (int x : s) { cout << x << endl; }
return 0; }
|
set 的查找
set
的查找可以使用 find
方法,其返回值是一个迭代器,如果 key
不存在,则返回 end()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> #include <set> using namespace std;
int main() { set<int> s{1, 2, 3};
set<int>::iterator it = s.find(2); if (it != s.end()) { cout << *it << endl; }
return 0; }
|
我们也可以使用 count
方法来判断 key
是否存在。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> #include <set> using namespace std;
int main() { set<int> s{1, 2, 3};
if (s.count(2)) { cout << "2 exists" << endl; }
return 0; }
|
除此之外,set
还有一个无序的版本 unordered_set
,其定义和使用方法与 set
类似,只是 unordered_set
是基于哈希表实现的,因此查找和插入的时间复杂度为O(1),而 set
是基于红黑树实现的,因此查找和插入的时间复杂度为O(logn)。
容器适配器
容器适配器是序列容器或关联容器的特殊变体,它们基于底层容器实现,但对接口做了更多限制,不支持迭代器,因此不能与 STL 算法一起使用。
queue
queue
是一个先进先出的队列,其元素只能从队尾插入,从队首删除,其默认基于 deque
实现,因此 queue
的插入和删除操作的时间复杂度为O(1)。
queue
以模板类的形式定义在头文件 <queue>
中,并位于 std
命名空间中,因此使用 queue
时需要包含头文件并使用 std
命名空间:
1 2
| #include <queue> using namespace std;
|
queue
的定义如下:
类型名可以是任意的 C++ 内置类型或自定义类型:
1 2
| queue<int> q1; queue<string> q2;
|
queue
定义时还可以指定初始元素。
1 2
| queue<int> q1{1, 2, 3}; queue<int> q2(q1);
|
queue 的常用方法
下表列出了 queue
的一些常用方法:
方法 | 说明 |
---|
q.empty() | 判断 q 是否为空 |
q.size() | 返回 q 的大小 |
q.push(x) | 插入元素 x 到 q 的队尾 |
q.pop() | 删除 q 中的队首元素 |
q.front() | 返回 q 中的队首元素 |
q.back() | 返回 q 中的队尾元素 |
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> #include <queue> using namespace std;
int main() { queue<int> q;
cout << q.empty() << endl; cout << q.size() << endl;
q.push(1); q.push(2); q.push(3); cout << q.empty() << endl; cout << q.size() << endl;
cout << q.front() << endl; cout << q.back() << endl;
q.pop(); cout << q.front() << endl;
return 0; }
|
queue
不支持随机访问,也不能像 vector
、deque
那样遍历,它只能通过 front
和 back
方法访问队首和队尾元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <iostream> #include <queue> using namespace std;
int main() { queue<int> q{1, 2, 3};
while (!q.empty()) { cout << q.front() << ' '; q.pop(); } cout << endl;
return 0; }
|
priority_queue
priority_queue
是一个优先队列,其元素按照优先级排序,优先级最高的元素在队首,优先级最低的元素在队尾,其默认基于 vector
实现,priority_queue
的插入和删除操作的时间复杂度为O(logn)。
priority_queue
以模板类的形式定义在头文件 <queue>
中,并位于 std
命名空间中,因此使用 priority_queue
时需要包含头文件并使用 std
命名空间:
1 2
| #include <queue> using namespace std;
|
priority_queue
的定义如下:
1
| priority_queue<类型名> 变量名;
|
类型名可以是任意的 C++ 内置类型或自定义类型:
1 2
| priority_queue<int> q1; priority_queue<string> q2;
|
priority_queue 的常用方法
下表列出了 priority_queue
的一些常用方法:
方法 | 说明 |
---|
q.empty() | 判断 q 是否为空 |
q.size() | 返回 q 的大小 |
q.push(x) | 插入元素 x 到 q |
q.pop() | 删除 q 中的队首元素 |
q.top() | 返回 q 中的队首元素 |
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> #include <queue> using namespace std;
int main() { priority_queue<int> q;
cout << q.empty() << endl; cout << q.size() << endl;
q.push(1); q.push(2); q.push(3); cout << q.empty() << endl; cout << q.size() << endl;
cout << q.top() << endl;
q.pop(); cout << q.top() << endl;
return 0; }
|
和 queue
一样,priority_queue
也不支持迭代器,因此访问元素的唯一方式是遍历容器,通过不断移除访问过的元素,去访问下一个元素。
priority_queue
默认为最大堆,即越大的元素优先级越高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <queue> using namespace std;
int main() { priority_queue<int> q; q.push(4); q.push(5); q.push(2); q.push(3); q.push(1);
while (!q.empty()) { cout << q.top() << " "; q.pop(); } cout << endl; return 0; }
|
如果想要实现最小堆,可以通过模板参数指定比较函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <queue> using namespace std;
int main() { priority_queue<int, vector<int>, greater<int>> q; q.push(4); q.push(5); q.push(2); q.push(3); q.push(1);
while (!q.empty()) { cout << q.top() << " "; q.pop(); } cout << endl; return 0; }
|
或是在插入和删除元素时对元素取反:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <queue> using namespace std;
int main() { priority_queue<int> q; q.push(-4); q.push(-5); q.push(-2); q.push(-3); q.push(-1);
while (!q.empty()) { cout << -q.top() << " "; q.pop(); } cout << endl; return 0; }
|
stack
stack
是一个栈,其元素按照先进后出的顺序排序,其默认基于 deque
实现,stack
的插入和删除操作的时间复杂度为O(1)。
stack
以模板类的形式定义在头文件 <stack>
中,并位于 std
命名空间中,因此使用 stack
时需要包含头文件并使用 std
命名空间:
1 2
| #include <stack> using namespace std;
|
stack
的定义如下:
类型名可以是任意的 C++ 内置类型或自定义类型:
1 2
| stack<int> s1; stack<string> s2;
|
stack 的常用方法
下表列出了 stack
的一些常用方法:
方法 | 说明 |
---|
s.empty() | 判断 s 是否为空 |
s.size() | 返回 s 的大小 |
s.push(x) | 插入元素 x 到 s |
s.pop() | 删除 s 中的栈顶元素 |
s.top() | 返回 s 中的栈顶元素 |
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> #include <stack> using namespace std;
int main() { stack<int> s;
cout << s.empty() << endl; cout << s.size() << endl;
s.push(1); s.push(2); s.push(3); cout << s.empty() << endl; cout << s.size() << endl;
cout << s.top() << endl;
s.pop(); cout << s.top() << endl;
return 0; }
|
和 queue
一样,stack
也不支持迭代器,因此访问元素的唯一方式是遍历容器,通过不断移除访问过的元素,去访问下一个元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <stack> using namespace std;
int main() { stack<int> s; s.push(1); s.push(2); s.push(3); s.push(4); s.push(5);
while (!s.empty()) { cout << s.top() << " "; s.pop(); } cout << endl; return 0; }
|
以上仅介绍了一些常用的 STL 容器,更多容器和使用方法可以参考 Microsoft C++ stl-containers 文档。
算法(Algorithm)
STL 中还提供了一些常用的算法,它们都定义在头文件 <algorithm>
中,并位于 std
命名空间中,因此使用算法时需要包含头文件并使用 std
命名空间:
1 2
| #include <algorithm> using namespace std;
|
下表列出了部分常用算法:
算法 | 说明 |
---|
sort(v.begin(), v.end()) | 对 v 中的元素进行排序 |
reverse(v.begin(), v.end()) | 将 v 中的元素反转 |
find(v.begin(), v.end(), x) | 在 v 中查找元素 x ,返回其迭代器 |
lower_bound(v.begin(), v.end(), x) | 在 v 中查找第一个大于等于 x 的元素,返回其迭代器,要求 v 有序 |
upper_bound(v.begin(), v.end(), x) | 在 v 中查找第一个大于 x 的元素,返回其迭代器,要求 v 有序 |
binary_search(v.begin(), v.end(), x) | 在 v 中查找元素 x ,返回 true 或 false ,要求 v 有序 |
max_element(v.begin(), v.end()) | 返回 v 中的最大元素的迭代器 |
min_element(v.begin(), v.end()) | 返回 v 中的最小元素的迭代器 |
accumulate(v.begin(), v.end(), x) | 返回 v 中所有元素的和,x 为初始值 |
copy(v.begin(), v.end(), v2.begin()) | 将 v 中的元素复制到 v2 中 |
count(v.begin(), v.end(), x) | 返回 v 中元素 x 的个数 |
count_if(v.begin(), v.end(), f) | 返回 v 中满足条件 f 的元素个数 |
fill(v.begin(), v.end(), x) | 将 v 中的所有元素赋值为 x |
replace(v.begin(), v.end(), x, y) | 将 v 中的所有元素 x 替换为 y |
replace_if(v.begin(), v.end(), f, x) | 将 v 中满足条件 f 的元素替换为 x |
unique(v.begin(), v.end()) | 将 v 中的重复元素移动至容器末尾,返回指向第一个重复元素的迭代器 |
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin()) | 将 v1 和 v2 合并到 v3 中 |
inplace_merge(v.begin(), v.begin() + n, v.end()) | 将 v 中的前 n 个元素和后面的元素合并 |
partition(v.begin(), v.end(), f) | 将 v 中满足条件 f 的元素放在前面,不满足的放在后面,返回指向第一个不满足条件 f 的元素的迭代器 |
random_shuffle(v.begin(), v.end()) | 将 v 中的元素随机打乱 |
next_permutation(v.begin(), v.end()) | 返回 v 的下一个排列,如果不存在下一个排列,则返回 false |
prev_permutation(v.begin(), v.end()) | 返回 v 的上一个排列,如果不存在上一个排列,则返回 false |
rotate(v.begin(), v.begin() + n, v.end()) | 将 v 中的元素循环左移 n 位 |
其中一些算法的使用示例代码如下:
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
| #include <iostream> #include <algorithm> #include <vector> using namespace std;
int main() { vector<int> v{1, 2, 3, 4, 5};
reverse(v.begin(), v.end()); for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl;
sort(v.begin(), v.end()); for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl;
vector<int>::iterator it = find(v.begin(), v.end(), 3); if (it != v.end()) { cout << "找到元素 3" << endl; } else { cout << "未找到元素 3" << endl; }
it = lower_bound(v.begin(), v.end(), 3); if (it != v.end()) { cout << "找到第一个大于等于 3 的元素:" << *it << endl; } else { cout << "未找到第一个大于等于 3 的元素" << endl; }
it = upper_bound(v.begin(), v.end(), 3); if (it != v.end()) { cout << "找到第一个大于 3 的元素:" << *it << endl; } else { cout << "未找到第一个大于 3 的元素" << endl; }
int sum = accumulate(v.begin(), v.end(), 0); cout << "v 中所有元素的和为:" << sum << endl;
srand(0); random_shuffle(v.begin(), v.end()); cout << "随机打乱后的 v:" << endl; for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl;
it = max_element(v.begin(), v.end()); cout << "v 中最大的元素为:" << *it << endl;
it = min_element(v.begin(), v.end()); cout << "v 中最小的元素为:" << *it << endl;
rotate(v.begin(), v.begin() + 1, v.end()); cout << "将 v 中的元素循环左移 1 位后的结果:" << endl; for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl;
next_permutation(v.begin(), v.end()); cout << "v 的下一个排列为:" << endl; for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; }
sort(v.begin(), v.end(), greater<int>()); cout << "将 v 中的元素降序排列后的结果:" << endl; for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl;
return 0; }
|
上面的示例仅展示了 STL 中部分常用算法的部分用法,更多函数和详细用法可以参考 Microsoft C++ <algorithms> 文档。
迭代器(Iterator)
迭代器是一种用于访问容器中元素的对象,类似指针,可以用来遍历容器中的元素。
迭代器按照定义可以分为四种:
- 正向迭代器:只能从前向后遍历容器中的元素,不能从后向前遍历。
- 常量正向迭代器:只能从前向后遍历容器中的元素,不能从后向前遍历,且只能读取容器中的元素,不能修改容器中的元素。
- 反向迭代器:只能从后向前遍历容器中的元素,不能从前向后遍历。
- 常量反向迭代器:只能从后向前遍历容器中的元素,不能从前向后遍历,且只能读取容器中的元素,不能修改容器中的元素。
它们的定义方法如下表所示:
迭代器类型 | 定义方法 |
---|
正向迭代器 | 容器类名::iterator 迭代器名; |
常量正向迭代器 | 容器类名::const_iterator 迭代器名; |
反向迭代器 | 容器类名::reverse_iterator 迭代器名; |
常量反向迭代器 | 容器类名::const_reverse_iterator 迭代器名; |
所有的迭代器都支持 ++
操作,即递增操作,用于访问容器中的下一个元素。对于正向迭代器,++
操作会使其指向容器中的后一个元素,而反向迭代器则是指向容器中的前一个元素。
使用 *迭代器名
可以访问迭代器所指向的元素,对于非常量迭代器,还可以使用 *迭代器名 = 新值
来修改迭代器所指向的元素。
<algorithm>
中的许多函数都是以迭代器作为返回值来返回的。
迭代器按功能分类可以分为三种:
- 正向迭代器:支持
++
、*
、==
、!=
操作,两个正向迭代器还可以相互赋值。 - 双向迭代器:支持正向迭代器的所有操作,还支持
--
操作,--
操作的移动方向与 ++
相反。 - 随机访问迭代器:支持双向迭代器的所有操作,还支持
+
、-
、+=
、-=
、<
、<=
、>
、>=
、[]
操作。
对于这些操作的简单解释如下:
操作 | 说明 |
---|
++ | 使迭代器指向容器中的下一个元素 |
-- | 使迭代器指向容器中的前一个元素 |
* | 返回迭代器所指向的元素 |
== | 判断两个迭代器是否指向同一个元素 |
!= | 判断两个迭代器是否指向不同的元素 |
+i | 返回原迭代器向后移动 i 个位置后的迭代器 |
-i | 返回原迭代器向前移动 i 个位置后的迭代器 |
+=i | 使原迭代器向后移动 i 个位置 |
-=i | 使原迭代器向前移动 i 个位置 |
< | 迭代器1 指向的元素是否在 迭代器2 指向的元素之前 |
<= | 迭代器1 指向的元素是否在 迭代器2 指向的元素之前或相等 |
> | 迭代器1 指向的元素是否在 迭代器2 指向的元素之后 |
>= | 迭代器1 指向的元素是否在 迭代器2 指向的元素之后或相等 |
[i] | 返回原迭代器后第 i 个元素 |
不同的容器支持的迭代器功能也不同,如下表所示:
容器 | 迭代器功能 |
---|
vector | 随机访问迭代器 |
deque | 随机访问迭代器 |
list | 双向迭代器 |
set | 双向迭代器 |
map | 双向迭代器 |
queue | 不支持迭代器 |
priority_queue | 不支持迭代器 |
stack | 不支持迭代器 |
使用容器自带的 begin()
和 end()
函数可以得到容器的首迭代器和尾迭代器,两迭代器相减可以得到它们在容器中的下标之差(可为负)。
下面是以 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
| #include <iostream> #include <algorithm> #include <vector> using namespace std;
int main() { vector<int> v{3, 1, 5, 4, 2};
for (auto it = v.begin(); it != v.end(); ++it) { cout << *it << " "; } cout << endl;
auto ma = max_element(v.begin(), v.end()); auto mi = min_element(v.begin(), v.end());
cout << "v 的最大值:" << *ma << endl; cout << "v 的最小值:" << *mi << endl; cout << "v 最大值与最小值元素下标差:" << ma - mi << endl;
return 0; }
|
更详细的迭代器使用方法可以参考 Microsoft C++ iterators 文档。