总结C++中的STL
-
1.STL 概述
STL 即标准模板库,是 C++ 标准程序库的重要组成部分,包含常用数据结构和算法,体现了泛型化程序设计思想,基于模板实现,提高了代码的可复用性
2.容器
2.1 序列容器:
1. vector
- 特性:动态数组,内存连续分配。随着元素的添加,若内存空间不足,会重新分配更大的内存块,并将原有元素复制过去。
- 优缺点:优点是支持随机访问,可通过下标快速访问任意位置的元素;在尾部插入和删除元素效率高。缺点是在中间或头部插入和删除元素效率低,因为需要移动后续元素。
- 常用操作:
-
#include <vector> #include <iostream> int main() {std::vector<int> vec;vec.push_back(10); // 尾部插入元素vec.insert(vec.begin(), 5); // 在开头插入元素std::cout << vec[1] << std::endl; // 随机访问元素vec.pop_back(); // 尾部删除元素return 0; }
2. deque
- 特性:双端队列,由多个固定大小的数组块组成,支持在两端快速插入和删除元素,同时也支持随机访问。
- 优缺点:优点是在两端插入和删除元素的时间复杂度为常数,适合需要频繁在两端操作的场景。缺点是随机访问的效率略低于
vector
。 - 常用操作:
-
#include <deque> #include <iostream> int main() {std::deque<int> dq;dq.push_front(1); // 头部插入元素dq.push_back(2); // 尾部插入元素std::cout << dq[0] << std::endl; // 随机访问元素dq.pop_front(); // 头部删除元素return 0; }
3. list
- 特性:双向链表,每个节点包含数据和指向前一个节点和后一个节点的指针。
- 优缺点:优点是在任意位置插入和删除元素的时间复杂度为常数,适合频繁插入和删除的操作。缺点是不支持随机访问,访问元素需要从头或尾开始遍历。
- 常用操作:
-
#include <list> #include <iostream> int main() {std::list<int> lst = {1, 2, 3};auto it = lst.begin();++it;lst.insert(it, 4); // 在指定位置插入元素lst.erase(it); // 删除指定位置元素for (int i : lst) {std::cout << i << " ";}return 0; }
2.2 关联容器:
1.set
- 特性:元素唯一且有序,底层通常实现为红黑树(一种自平衡二叉搜索树)。插入、删除和查找元素的时间复杂度为 O(logn)。
- 优缺点:优点是元素自动排序,且能保证元素的唯一性。缺点是插入和查找操作的时间复杂度不是常数级。
- 常用操作:
#include <set>
#include <iostream>
int main() {std::set<int> s;s.insert(3);s.insert(1);auto it = s.find(1); // 查找元素if (it != s.end()) {std::cout << "Found" << std::endl;}return 0;
}
2.multiset
- 特性:与
set
类似,但允许元素重复。 - 常用操作:基本与
set
相同,只是插入重复元素时不会被忽略。
3.map
- 特性:键值对的集合,键唯一且有序,底层同样是红黑树。可通过键快速查找对应的值。
- 优缺点:优点是查找和插入操作的时间复杂度为 O(logn),且能保证键的有序性。缺点是空间开销较大。
- 常用操作:
#include <map>
#include <iostream>
#include <string>
int main() {std::map<std::string, int> m;m["apple"] = 10; // 插入键值对auto it = m.find("apple"); // 查找键if (it != m.end()) {std::cout << it->second << std::endl; // 输出对应的值}return 0;
}
4.multimap
- 特性:与
map
类似,但允许键重复。
2.2 无序容器
1.unordered_set
- 特性:无序集合,元素唯一,底层实现为哈希表。插入、删除和查找元素的平均时间复杂度为 O(1)。
- 优缺点:优点是查找和插入操作的平均时间复杂度为常数级。缺点是元素无序,且哈希冲突可能会影响性能。
- 常用操作:
#include <unordered_set>
#include <iostream>
int main() {std::unordered_set<int> us;us.insert(3);auto it = us.find(3); // 查找元素if (it != us.end()) {std::cout << "Found" << std::endl;}return 0;
}
2.unordered_multiset
- 特性:与
unordered_set
类似,但允许元素重复。
3.unordered_map
- 特性:无序键值对集合,底层为哈希表。适合快速查找键对应的值。
- 常用操作:
#include <unordered_map>
#include <iostream>
#include <string>
int main() {std::unordered_map<std::string, int> um;um["apple"] = 10; // 插入键值对auto it = um.find("apple"); // 查找键if (it != um.end()) {std::cout << it->second << std::endl; // 输出对应的值}return 0;
}
4.unordered_multimap
- 特性:与
unordered_map
类似,但允许键重复。
3. 迭代器(Iterators)
3.1 输入迭代器
- 特性:只能用于读取容器中的元素,不支持写入操作,且只能单向移动,每次移动一步。例如
find
算法使用输入迭代器来查找元素。
#include <vector>
#include <algorithm>
#include <iostream>
int main() {std::vector<int> vec = {1, 2, 3};auto it = std::find(vec.begin(), vec.end(), 2);if (it != vec.end()) {std::cout << *it << std::endl;}return 0;
}
3.2 输出迭代器
- 特性:只能用于向容器中写入元素,不支持读取操作,同样只能单向移动,每次移动一步。例如
copy
算法使用输出迭代器将元素从一个容器复制到另一个容器。
#include <vector>
#include <algorithm>
#include <iostream>
int main() {std::vector<int> src = {1, 2, 3};std::vector<int> dest(3);std::copy(src.begin(), src.end(), dest.begin());for (int i : dest) {std::cout << i << " ";}return 0;
}
3.3 前向迭代器
- 特性:支持读取和写入操作,并且可以向前移动,允许多次遍历容器。例如
forward_list
的迭代器就是前向迭代器。
#include <forward_list>
#include <iostream>
int main() {std::forward_list<int> fl = {1, 2, 3};for (auto it = fl.begin(); it != fl.end(); ++it) {*it *= 2; // 写入操作}for (int i : fl) {std::cout << i << " ";}return 0;
}
3.4 双向迭代器
- 特性:支持读取、写入和双向移动,可用于双向链表等数据结构。例如
list
的迭代器就是双向迭代器。
#include <list>
#include <iostream>
int main() {std::list<int> lst = {1, 2, 3};auto it = lst.end();--it; // 反向移动std::cout << *it << std::endl;return 0;
}
3.5.随机访问迭代器
- 特性:支持随机访问,可通过下标快速访问任意位置的元素,还支持双向移动和跳跃移动。例如
vector
的迭代器就是随机访问迭代器。
#include <vector>
#include <iostream>
int main() {std::vector<int> vec = {1, 2, 3};auto it = vec.begin() + 2; // 跳跃移动std::cout << *it << std::endl;return 0;
}
4.算法
4.1 排序算法:
1.sort
- 功能:对指定范围内的元素进行排序,默认使用升序排序。
- 示例:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {std::vector<int> vec = {3, 1, 2};std::sort(vec.begin(), vec.end());for (int i : vec) {std::cout << i << " ";}return 0;
}
2.stable_sort
- 功能:与
sort
类似,但能保证相等元素的相对顺序不变。
4.2 查找算法:
1.find
- 功能:在指定范围内查找指定值的元素,返回第一个匹配元素的迭代器。
- 示例:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {std::vector<int> vec = {1, 2, 3};auto it = std::find(vec.begin(), vec.end(), 2);if (it != vec.end()) {std::cout << "Found" << std::endl;}return 0;
}
2.binary_search
- 功能:在有序范围内进行二分查找,判断指定值是否存在。
- 示例:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {std::vector<int> vec = {1, 2, 3};bool found = std::binary_search(vec.begin(), vec.end(), 2);if (found) {std::cout << "Found" << std::endl;}return 0;
}
4.3 遍历算法:
1. for_each:对容器中的每个元素执行指定的函数或函数对象。
#include <vector>
#include <algorithm>
#include <iostream>
void print(int i) {std::cout << i << " ";
}
int main() {std::vector<int> vec = {1, 2, 3};std::for_each(vec.begin(), vec.end(), print);return 0;
}
2. transform:将一个容器中的元素按照指定的规则转换为另一个容器中的元素。
4.4 数值算法:
1. accumulate:计算容器中元素的总和,还可以指定一个初始值,时间复杂度为 O(n)。
示例:
#include <vector>
#include <numeric>
#include <iostream>
int main() {std::vector<int> vec = {1, 2, 3};int sum = std::accumulate(vec.begin(), vec.end(), 0);std::cout << sum << std::endl;return 0;
}
2. inner_product:计算两个容器对应元素的乘积之和,可用于计算向量的内积等。
4.5.仿函数
仿函数是一个重载了函数调用运算符 ()
的类或结构体。
可以像函数一样被调用,并且可以携带状态信息。
在 STL 算法中,常作为参数来定制算法的行为,例如定义比较规则、计算规则等。
1.一元仿函数
- 示例:定义一个仿函数用于判断元素是否为偶数。
#include <vector>
#include <algorithm>
#include <iostream>
struct IsEven {bool operator()(int num) const {return num % 2 == 0;}
};
int main() {std::vector<int> vec = {1, 2, 3};auto it = std::find_if(vec.begin(), vec.end(), IsEven());if (it != vec.end()) {std::cout << *it << std::endl;}return 0;
}
2.二元仿函数
- 示例:定义一个仿函数用于降序排序。
#include <vector>
#include <algorithm>
#include <iostream>
struct GreaterThan {bool operator()(int a, int b) const {return a > b;}
};
int main() {std::vector<int> vec = {1, 2, 3};std::sort(vec.begin(), vec.end(), GreaterThan());for (int i : vec) {std::cout << i << " ";}return 0;
}
6.适配器
6.1 容器适配器:
1.stack:栈适配器,基于 deque 或 vector 实现,提供了后进先出(LIFO)的存储方式。
- 常用操作:
#include <stack>
#include <iostream>
int main() {std::stack<int> st;st.push(1);st.push(2);std::cout << st.top() << std::endl; // 访问栈顶元素st.pop(); // 弹出栈顶元素return 0;
}
2.queue:队列适配器,基于 deque 实现,提供了先进先出(FIFO)的存储方式。
- 常用操作:
#include <queue>
#include <iostream>
int main() {std::queue<int> q;q.push(1);q.push(2);std::cout << q.front() << std::endl; // 访问队首元素q.pop(); // 弹出队首元素return 0;
}
3.priority_queue:优先队列适配器,基于 vector 实现,元素按照优先级进行存储和访问。
- 常用操作:
#include <queue>
#include <iostream>
int main() {std::priority_queue<int> pq;pq.push(3);pq.push(1);std::cout << pq.top() << std::endl; // 访问队首元素pq.pop(); // 弹出队首元素return 0;
}
6.2 迭代器适配器:
1.reverse_iterator:反向迭代器,用于反向遍历容器,通过将正向迭代器进行封装,实现了与正向迭代器相反的遍历顺序。
示例:
#include <vector>
#include <iostream>
int main() {std::vector<int> vec = {1, 2, 3};for (auto it = vec.rbegin(); it != vec.rend(); ++it) {std::cout << *it << " ";}return 0;
}
2.back_insert_iterator:后插入迭代器,用于在容器尾部插入元素,常用于将元素插入到一个动态增长的容器中。
6.3 仿函数适配器:
1.bind:用于绑定仿函数的参数,将一个多参数的仿函数转换为一个少参数的仿函数。
示例:
#include <iostream>
#include <functional>
int add(int a, int b) {return a + b;
}
int main() {auto add5 = std::bind(add, 5, std::placeholders::_1);std::cout << add5(3) << std::endl; // 输出 8return 0;
}