std::list 和 std::vector 是 C++ 标准库中的两种不同类型的容器,它们各有其特点和适用场景。
std::vector
std::vector 是一个动态数组,它支持在数组的末尾进行快速的插入和删除操作。std::vector 在内存中连续存储元素,因此可以通过索引直接访问任何元素。由于其连续存储的特性,std::vector 在处理大量数据时通常比链表等数据结构更高效。
特点:
- 连续存储元素,支持随机访问。
- 在尾部插入和删除元素的速度很快(摊还时间复杂度为 O(1))。
- 在中间插入和删除元素的速度较慢(时间复杂度为 O(n)),因为需要移动元素。
- 提供了丰富的成员函数,如 push_back、pop_back、insert、erase 等。
示例:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
// 向尾部插入元素
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
// 访问元素
std::cout << "Element at index 1: " << vec[1] << std::endl;
// 在尾部删除元素
vec.pop_back();
// 在中间插入元素
vec.insert(vec.begin() + 1, 4);
// 遍历元素
for (const auto& element : vec) {
std::cout << element << " ";
}
std::cout << std::endl;
return 0;
}
std::vector reserve
/**
@brief尝试为指定数量的元素预先分配足够的内存。
@param __n所需的元素数。
@throw std::length_error如果@a n超过@c max_size()。
此函数试图为向量保留足够的内存,以容纳指定数量的元素。如果请求的数字大于max_size(),则抛出length_error。
此功能的优点是,如果需要最佳代码,并且用户可以确定所需元素的数量,则用户可以提前保留内存,从而防止可能的内存重新分配和矢量数据的复制。
*/
void reserve(size_type __n);
std::vector resize
std::vector::resize 是 C++ 标准库中 std::vector 容器的一个成员函数,用于改变容器的大小。
函数原型如下:
void resize(size_type new_size);
void resize(size_type new_size, const value_type& c);
两个原型的区别在于是否提供了新元素的值。
-
当调用 resize(n) 时,会使容器的大小变为 n,如果 n 小于当前大小,则容器尾部的元素会被移除;如果 n 大于当前大小,则在容器尾部添加足够多的元素,新添加的元素的值默认初始化(对于内置类型会是零)。
-
当调用 resize(n, val) 时,效果和上面一样,但是对于新添加的元素会使用值 val 来初始化。
std::list
std::list 是一个双向链表,它支持在链表的任何位置进行快速的插入和删除操作。由于std::list的元素不是连续存储的,因此它不支持通过索引直接访问元素,而只能通过迭代器访问。
特点:
- 元素不是连续存储的,不支持随机访问。
- 在链表头部和尾部插入和删除元素的速度很快(时间复杂度为 O(1))。
- 在链表中间插入和删除元素的速度也很快(摊还时间复杂度为 O(1)),因为不需要移动其他元素。
- 提供了迭代器来遍历链表。
示例:
#include <iostream>
#include <list>
int main() {
std::list<int> lst;
// 向链表尾部插入元素
lst.push_back(1);
lst.push_back(2);
lst.push_back(3);
// 访问第一个元素
std::cout << "First element: " << lst.front() << std::endl;
// 访问最后一个元素
std::cout << "Last element: " << lst.back() << std::endl;
// 在链表头部插入元素
lst.push_front(0);
// 在链表中间插入元素
auto it = lst.begin();
++it; // 移动到第二个元素之前
lst.insert(it, 4);
// 遍历链表
for (const auto& element : lst) {
std::cout << element << " ";
}
std::cout << std::endl;
return 0;
}
选择:
- 当你需要频繁地在序列中间插入和删除元素时,std::list 通常是一个更好的选择。
- 当你需要快速访问序列中的任何元素,并且不需要频繁地在中间插入和删除元素时,std::vector 通常更合适。
- 对于小型到中型数据集,std::vector 往往比 std::list 表现得更好,因为它在内存中是连续的,可以利用 CPU 的缓存。
- std::vector 还支持 reserve 和 capacity 函数来预先分配内存,这对于处理大量数据且知道最终大小的情况非常有用,可以避免频繁的内存重新分配。
std::map和std::unordered_map
在C++中,我们通常使用std::map或std::unordered_map来实现字典的功能。std::map是基于红黑树实现的关联容器,而std::unordered_map则是基于哈希表实现的关联容器。
下面是std::map和std::unordered_map的基本使用示例:
std::map
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> dict;
// 插入键值对
dict["apple"] = 1;
dict["banana"] = 2;
dict["cherry"] = 3;
// 访问键值对
std::cout << "apple: " << dict["apple"] << std::endl;
// 遍历字典
for (const auto& pair : dict) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
std::unordered_map
#include <iostream>
#include <unordered_map>
int main() {
std::unordered_map<std::string, int> dict;
// 插入键值对
dict["apple"] = 1;
dict["banana"] = 2;
dict["cherry"] = 3;
// 访问键值对
std::cout << "apple: " << dict["apple"] << std::endl;
// 遍历字典
for (const auto& pair : dict) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
这两个示例中,我们都创建了一个字符串到整数的映射,并插入了一些键值对。然后,我们访问了一个键的值,并遍历了字典中的所有键值对。
需要注意的是,std::map的遍历顺序是按照键的升序排列的,而std::unordered_map的遍历顺序则是随机的,因为它基于哈希表实现。
此外,std::map和std::unordered_map都提供了find函数来查找键是否存在于字典中,erase函数来删除键值对,以及insert函数来插入新的键值对。