1、什么是vector
std::vector
是C++标准库(STL)中的一个动态数组类模板。它允许存储相同类型的元素集合,并且可以根据需要动态地增加或减少其大小。std::vector
提供了一组函数来访问、插入和删除元素,以及执行其他与数组操作相关的任务。使用 std::vector
可以避免手动管理动态数组时可能遇到的许多常见问题,如内存泄漏和越界访问。
2、vector背后实现原理
std::vector
通常在内部使用一个连续的内存块来存储元素。这意味着它的元素在内存中是顺序排列的,这有助于快速访问和迭代。当 std::vector
需要增长时,它会分配一个更大的内存块,通常大小是原容量的两倍或更多,然后将旧元素复制到新内存块中,并释放旧内存块。这种增长策略有助于减少内存分配和复制操作的次数,但也可能导致一些额外的内存浪费。
3、vector都有哪些操作API,并举例介绍api的使用方法
std::vector
提供了丰富的API来操作元素,以下是一些常用的API及其使用方法:
(1)构造函数
std::vector<int> vec; // 默认构造函数,创建一个空的vector
std::vector<int> vec(10); // 创建一个包含10个元素的vector,默认初始化为0
std::vector<int> vec(10, 5); // 创建一个包含10个元素的vector,每个元素初始化为5
std::vector<int> vec2(vec); // 通过另一个vector复制构造
(2)添加和插入元素
vec.push_back(42); // 在vector末尾添加一个元素
vec.insert(vec.begin(), 100); // 在vector开始位置插入一个元素
vec.emplace_back(1, 2, 3); // 在vector末尾构造并添加一个元素(使用构造函数参数)
(3)访问元素
int first = vec[0]; // 通过下标访问元素
int last = vec.back(); // 访问vector的最后一个元素
int atThird = vec.at(2); // 通过at函数访问元素(会进行范围检查)
(4)删除元素
vec.pop_back(); // 删除vector的最后一个元素
vec.erase(vec.begin()); // 删除vector的第一个元素
vec.erase(vec.begin() + 1, vec.end() - 1); // 删除一个范围内的元素
vec.clear(); // 清空vector,删除所有元素
(5)修改元素大小
vec.resize(20); // 改变vector的大小为20,如果新大小大于当前大小,则新元素默认初始化为0
vec.resize(10, 100); // 改变vector的大小为10,如果新大小小于当前大小,则删除多余元素;如果新大小大于当前大小,则新元素初始化为100
(6)容量和大小
size_t size = vec.size(); // 获取vector中元素的个数
size_t capacity = vec.capacity(); // 获取vector的容量(已分配的内存大小)
vec.shrink_to_fit(); // 尝试减少vector的容量以匹配其大小
(7)迭代器
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) { std::cout << *it << " ";
}
std::cout << std::endl; // 使用范围for循环
for (const auto& elem : vec) { std::cout << elem << " ";
}
std::cout << std::endl;
4、时间复杂度和空间复杂度
(1)空间复杂度
std::vector
的空间复杂度是 O(n),其中 n 是当前存储在 vector
中的元素数量。这是因为 vector
需要为其所有元素分配连续的内存空间。此外,vector
还可能分配额外的内存空间以支持未来的增长,但这部分空间仍然与元素数量成线性关系。
(2)时间复杂度
std::vector
的时间复杂度取决于你执行的具体操作:
-
访问元素:通过下标访问元素(如
vec[i]
)是常数时间复杂度 O(1),因为元素在内存中是连续存储的,可以直接通过计算偏移量来访问。 -
在末尾添加或删除元素:对于
push_back
、pop_back
等在vector
末尾进行的操作,如果不需要重新分配内存,那么它们是常数时间复杂度 O(1)。然而,如果当前容量不足以容纳新元素,vector
会重新分配内存并复制现有元素到新位置,这种情况下时间复杂度可能达到 O(n)。但是,重新分配是摊还常数时间的,因为它不会每次都发生,且平均下来每次插入或删除操作的时间复杂度仍然是常数。 -
在任意位置插入或删除元素:对于
insert
和erase
等在vector
中间位置进行的操作,时间复杂度是 O(n),因为可能需要移动插入点或删除点之后的所有元素。 -
改变
vector
的大小:使用resize
函数改变vector
的大小,如果新大小大于当前大小,则可能需要构造新的元素(时间复杂度与新增元素的数量成正比);如果新大小小于当前大小,则需要删除多余的元素(时间复杂度与删除元素的数量成正比)。因此,时间复杂度至少是 O(k),其中 k 是改变的大小量。
需要注意的是,虽然 std::vector
在某些情况下可能需要进行内存重新分配和元素复制,但这些操作的开销通常是摊还的,即它们在平均情况下的时间复杂度仍然是相对较低的。因此,std::vector
仍然是一种高效且常用的动态数组实现。
总的来说,std::vector
的空间复杂度是 O(n),而时间复杂度则取决于你执行的具体操作以及是否需要重新分配内存。在实际应用中,由于 vector
提供了高效的内存管理和访问机制,它通常是一个很好的选择,尤其是在需要频繁访问元素或添加元素到末尾的情况下。