C++ Standard Template Library (STL) 是 C++ 标准库的一个重要组成部分,提供了一系列高效、通用、可复用的模板类和函数。STL 极大地提高了 C++ 程序员的开发效率,使得许多常见的数据结构和算法问题可以通过简单的几行代码解决。本文将详细介绍 STL 中的一些常见组件及其使用方法。

容器(Containers)

向量(vector)

std::vector 是最常用的动态数组实现,支持随机访问,能够在运行时动态地增加和减少元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <vector>  
#include <iostream>

int main() {
std::vector<int> vec; // 创建一个空的整数向量
vec.push_back(1); // 向向量末尾添加一个元素
vec.push_back(2);
vec.push_back(3);

for (int i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << " "; // 使用下标访问元素
}
std::cout << std::endl;

// 使用范围for循环
for (const auto &elem : vec) {
std::cout << elem << " ";
}
std::cout << std::endl;

return 0;
}

列表(list)

std::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
#include <list>  
#include <iostream>

int main() {
std::list<int> lst = {1, 2, 3}; // 初始化列表

for (auto it = lst.begin(); it != lst.end(); ++it) {
std::cout << *it << " "; // 使用迭代器访问元素
}
std::cout << std::endl;

lst.push_back(4); // 在列表末尾添加元素
lst.insert(lst.begin(), 0); // 在列表开始处插入元素

auto it = lst.find(2); // 查找元素
if (it != lst.end()) {
lst.erase(it); // 删除找到的元素
}

for (const auto &elem : lst) {
std::cout << elem << " ";
}
std::cout << std::endl;

return 0;
}

映射(map)

std::map 是一个关联数组,它存储的元素是键值对,并且按键进行排序。

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 <map>  
#include <iostream>

int main() {
std::map<std::string, int> ages;

ages["Alice"] = 25;
ages["Bob"] = 30;

std::cout << "Alice's age: " << ages["Alice"] << std::endl;

// 查找元素
auto it = ages.find("Bob");
if (it != ages.end()) {
std::cout << "Bob's age: " << it->second << std::endl;
}

// 遍历 map
for (const auto &pair : ages) {
std::cout << pair.first << ": " << pair.second << std::endl;
}

return 0;
}

算法(Algorithms)

STL 提供了一系列高效的算法,这些算法通常独立于容器类型,通过迭代器操作数据。

排序(sort)

std::sort 是最常用的排序算法,可以对数组或容器进行排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <vector>  
#include <algorithm>
#include <iostream>

int main() {
std::vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

std::sort(nums.begin(), nums.end()); // 对向量进行排序

for (int num : nums) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

查找(find)

std::find 用于在容器中查找特定元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <vector>  
#include <algorithm>
#include <iostream>

int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};

auto it = std::find(nums.begin(), nums.end(), 3); // 查找元素3

if (it != nums.end()) {
std::cout << "Found " << *it << std::endl;
} else {
std::cout << "Not found" << std::endl;
}

return 0;
}

转换(transform)

std::transform 用于对容器中的每个元素应用一个函数,并产生一个新的容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <vector>  
#include <algorithm>
#include <iterator>
#include <iostream>

int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};
std::vector<int> squares;

// 预留足够的空间以提高性能
squares.reserve(nums.size());

// 使用 std::transform 将每个元素平方并存入新容器
std::transform(nums.begin(), nums.end(), std::back_inserter(squares),
[](int x) { return x * x; });

for (int square : squares) {
std::cout << square << " ";
}
std::cout << std::endl;

return 0;
}

C++ STL(Standard Template Library)除了上述常见的容器如 vectorlistmap 之外,还提供了其他一些非常有用的容器。以下是其中一些容器的简要说明:

1. set

set 是一个包含唯一元素的容器,它通常使用红黑树实现,元素会自动按键排序。你不能在 set 中插入重复的元素。set 中的每个元素只能出现一次。

2. multiset

set 类似,multiset 也包含按键排序的元素,但 multiset 允许元素重复出现,即它可以包含多个相同的元素。

3. unordered_set

unordered_set 是一个不包含重复元素的容器,但它不保证元素的排序。它通常使用哈希表实现,因此插入、删除和查找操作的时间复杂度可以接近 O(1)。

4. unordered_multiset

unordered_multisetunordered_set 类似,但它允许元素重复出现。它不保证元素的排序,并且通常使用哈希表实现。

5. deque

deque(双端队列)是一个支持在两端高效插入和删除元素的容器。与 vector 不同,deque 并没有将所有元素存储在连续的内存中,因此它不能保证随机访问的常数时间复杂度。

6. stack

stack 是一个后进先出(LIFO)的容器适配器,它提供了栈的基本操作,如 push(入栈)、pop(出栈)和 top(查看栈顶元素)。stack 通常使用 deque 作为底层容器。

7. queue

queue 是一个先进先出(FIFO)的容器适配器,它提供了队列的基本操作,如 push(入队)、pop(出队)和 front(查看队首元素)。queue 通常使用 deque 作为底层容器。

8. priority_queue

priority_queue 是一个允许用户根据元素的优先级进行插入和删除操作的容器适配器。元素的优先级通过比较函数确定,优先级最高的元素总是位于队列的前端。priority_queue 通常使用 vectormake_heap 算法作为底层实现。

这些容器提供了不同的功能和性能特性,你可以根据具体的需求选择合适的容器来解决问题。需要注意的是,除了 stackqueuepriority_queue 是容器适配器外,其他容器都是直接可用的数据结构。容器适配器是对其他容器的封装,提供了特定用途的接口。