C++ STL容器概述:vector、list、deque使用
·

在C++编程的世界里,标准模板库(STL)是一个强大的工具集,它为我们提供了丰富的数据结构和算法。而容器作为STL的重要组成部分,就像是一个个功能各异的“收纳盒”,可以帮助我们高效地管理和操作数据。在这一小节中,我们将重点介绍三种常见的STL容器:vector、list和deque,了解它们的特点、使用方法,并通过实际的代码示例来帮助你更好地掌握。
目录
常见STL容器的特点
vector
vector是一种动态数组,它的特点就像一个可以自动扩容的“弹性数组”。它支持随机访问,也就是说你可以像访问普通数组一样,通过下标快速地访问任意位置的元素。
- 连续内存存储:
vector中的元素在内存中是连续存储的,这就好比一排紧密排列的房子,你可以很方便地根据门牌号(下标)找到对应的房子(元素)。这种存储方式使得随机访问的效率非常高,时间复杂度为O(1)。例如,如果你有一个vector<int>类型的容器vec,你可以通过vec[3]快速访问到第4个元素。 - 动态扩容:当
vector的容量不足时,它会自动分配更大的内存空间,并将原有的元素复制到新的空间中。就像你原来的房子住不下了,会搬到一个更大的房子里,并把原来的家具都搬过去。不过,扩容操作会带来一定的性能开销,因为需要进行内存分配和元素复制。
list
list是一种双向链表,它的每个节点都包含一个元素和指向前一个节点和后一个节点的指针。可以把它想象成一列火车,每节车厢(节点)都和前后车厢相连。
- 双向链表结构:由于
list是双向链表,插入和删除元素的效率非常高,时间复杂度为O(1)。无论在链表的任何位置插入或删除元素,只需要修改相邻节点的指针即可,就像在火车中间添加或移除一节车厢一样,不需要移动其他车厢。 - 不支持随机访问:与
vector不同,list不支持随机访问。你不能像通过下标访问vector元素那样直接访问list中的元素。如果你要访问list中的某个元素,需要从链表的头部或尾部开始,逐个节点遍历,时间复杂度为O(n)。
deque
deque是一种双端队列,它结合了vector和list的部分特点。可以把它想象成一个两端都可以进出的通道。
- 双端插入和删除高效:
deque支持在队列的头部和尾部高效地插入和删除元素,时间复杂度为O(1)。就像在通道的两端都可以快速地进出物品一样。 - 随机访问:
deque也支持随机访问,虽然效率比vector略低,但仍然可以通过下标快速访问元素。它的内存存储方式是分段连续的,类似于多个vector连接在一起。
常见STL容器的使用方法
容器的创建
下面是创建vector、list和deque容器的基本示例:
#include <iostream>
#include <vector>
#include <list>
#include <deque>
int main() {
// 创建一个空的vector
std::vector<int> vec;
// 创建一个包含5个元素的vector,初始值都为10
std::vector<int> vec2(5, 10);
// 创建一个空的list
std::list<int> lst;
// 创建一个包含3个元素的list,初始值都为20
std::list<int> lst2(3, 20);
// 创建一个空的deque
std::deque<int> dq;
// 创建一个包含4个元素的deque,初始值都为30
std::deque<int> dq2(4, 30);
return 0;
}
元素的插入
不同的容器有不同的插入方法:
#include <iostream>
#include <vector>
#include <list>
#include <deque>
int main() {
// vector的插入
std::vector<int> vec;
vec.push_back(1); // 在尾部插入元素
vec.insert(vec.begin() + 1, 2); // 在指定位置插入元素
// list的插入
std::list<int> lst;
lst.push_back(3); // 在尾部插入元素
lst.push_front(4); // 在头部插入元素
auto it = lst.begin();
++it;
lst.insert(it, 5); // 在指定位置插入元素
// deque的插入
std::deque<int> dq;
dq.push_back(6); // 在尾部插入元素
dq.push_front(7); // 在头部插入元素
dq.insert(dq.begin() + 1, 8); // 在指定位置插入元素
return 0;
}
元素的删除
同样,不同容器的删除方法也有所不同:
#include <iostream>
#include <vector>
#include <list>
#include <deque>
int main() {
// vector的删除
std::vector<int> vec = {1, 2, 3, 4};
vec.pop_back(); // 删除尾部元素
vec.erase(vec.begin() + 1); // 删除指定位置的元素
// list的删除
std::list<int> lst = {5, 6, 7, 8};
lst.pop_back(); // 删除尾部元素
lst.pop_front(); // 删除头部元素
auto it = lst.begin();
++it;
lst.erase(it); // 删除指定位置的元素
// deque的删除
std::deque<int> dq = {9, 10, 11, 12};
dq.pop_back(); // 删除尾部元素
dq.pop_front(); // 删除头部元素
dq.erase(dq.begin() + 1); // 删除指定位置的元素
return 0;
}
避免容器使用中的内存管理和迭代器失效问题
内存管理
在使用vector时,由于它的动态扩容机制,可能会导致内存碎片和性能开销。为了避免这些问题,你可以在创建vector时预先分配足够的内存空间,使用reserve方法。例如:
#include <vector>
int main() {
std::vector<int> vec;
vec.reserve(100); // 预先分配100个元素的内存空间
for (int i = 0; i < 100; ++i) {
vec.push_back(i);
}
return 0;
}
迭代器失效
在使用容器的迭代器时,插入和删除元素可能会导致迭代器失效。例如,在vector中插入元素后,原有的迭代器可能会指向无效的内存地址。为了避免迭代器失效,你需要在插入或删除元素后,及时更新迭代器。例如:
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4};
auto it = vec.begin();
++it;
it = vec.insert(it, 5); // 插入元素后更新迭代器
for (auto num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
总结与后续内容
通过本小节的学习,你已经了解了vector、list和deque这三种常见STL容器的特点和使用方法,学会了如何创建容器、插入和删除元素,并且知道了如何避免内存管理和迭代器失效的问题。掌握了这些内容后,下一节我们将深入学习STL中的其他容器,如map、set等,进一步完善对本章C++标准模板库(STL)主题的认知。

** 🍃 系列专栏导航**
- 🍃 博客概览:《程序员技术成长导航,专栏汇总》
更多推荐
所有评论(0)