- 序列容器(Sequential Containers)
vector
- 特点:
- 可以简单理解为一个动态数组,它在内存中是连续存储的。这使得它支持快速随机访问,通过索引访问元素的时间复杂度为常数时间 。例如,对于一个
vector<int>
类型的容器v
,可以使用v[3]
来快速访问第 4 个元素(索引从 0 开始)。 - 但是,在中间或头部插入 / 删除元素可能比较耗时,因为需要移动插入 / 删除位置之后的所有元素。插入操作在尾部比较高效,平均时间复杂度为常数时间(如果不考虑可能的内存重新分配情况)。
- 适用场景:
- 适用于需要频繁访问元素,且元素数量变化不太频繁(尤其是在中间位置的插入和删除操作不多)的场景。例如,存储一个图形程序中的顶点坐标列表,这些坐标在初始化后通常只是读取用于绘制,很少在中间插入或删除新的坐标。
list
- 特点:
- 如前面所说,它是一个双向链表。每个节点包含数据和指向前一个节点、后一个节点的指针。这使得它在任何位置插入和删除元素都比较高效,时间复杂度为 ,因为只需要修改节点之间的指针关系。
- 但是,随机访问元素比较慢,需要从链表头(或尾)开始遍历,访问第 个元素的时间复杂度为 。
- 适用场景:
- 适合需要频繁插入和删除元素,而对随机访问要求不高的场景。例如,在一个任务调度系统中,任务可能会频繁地添加和移除,用
list
来存储任务列表可以高效地处理这些操作。
deque
(双端队列)
- 特点:
- 是一种双端队列容器,它的名字是 “double - ended queue” 的缩写。它允许在两端(头部和尾部)快速插入和删除元素,时间复杂度为 。
- 它在内存中的存储方式与
vector
不同,不是连续的单一内存块,而是由多个固定大小的块组成,这使得它在两端的操作性能较好,但随机访问性能不如vector
,不过仍然比list
好一些,访问元素的时间复杂度为 (在最坏情况下,是元素个数)。
- 适用场景:
- 适用于需要在两端高效操作的场景,如实现一个简单的滑动窗口算法,在窗口的两端添加和移除数据点。
forward_list
(单向链表)
- 特点:
- 这是一个单向链表容器,每个节点只包含一个指向下一个节点的指针。相比
list
,它的内存占用可能会稍小一些,因为少了一个指针。它在头部插入和删除元素非常快,时间复杂度为 ,但在其他位置插入和删除操作相对复杂,因为需要遍历找到插入位置的前一个节点。 - 由于是单向链表,它不支持反向遍历,随机访问性能也很差,访问第 个元素的时间复杂度为 。
- 适用场景:
- 适用于只需要单向遍历,且主要在头部进行插入和删除操作的场景。例如,实现一个简单的事件队列,事件按照产生的顺序添加到队列头部,然后从头部依次处理。
- 关联容器(Associative Containers)
map
- 特点:
map
是一个键 - 值对容器,它按照键(key
)进行排序(默认是升序)。键是唯一的,通过键可以快速查找对应的的值,查找、插入和删除操作的时间复杂度在平均情况下为 (是元素个数)。- 例如,对于一个
map<int, string>
类型的容器m
,可以通过m[1]
来获取键为 1 对应的字符串值(如果键不存在,会插入一个默认值并返回这个默认值,这可能会导致意外的插入操作,所以有时候需要先检查键是否存在)。
- 适用场景:
- 适用于需要根据键来快速查找、插入和删除值的场景。比如,存储一个学生的学号(键)和姓名(值)的对应关系,通过学号快速查找学生姓名。
multimap
- 特点:
- 与
map
类似,也是键 - 值对容器,但是键可以不唯一,即可以有多个键相同的键 - 值对。查找、插入和删除操作的时间复杂度在平均情况下也为 。 - 当查找一个键时,会返回所有具有该键的键 - 值对范围。例如,在一个存储单词(键)和出现位置(值)的
multimap
中,查找一个单词时,可以得到这个单词在文本中所有出现的位置。
- 适用场景:
- 适用于需要存储多个具有相同键的键 - 值对的场景,如统计一个文本中每个单词出现的次数和位置。
set
- 特点:
set
是一个只存储键的容器,键是唯一的,并且会按照一定顺序(默认升序)自动排序。它的主要操作包括插入、删除和查找,时间复杂度在平均情况下为 。- 例如,对于一个
set<int>
类型的容器s
,可以使用count
函数检查一个整数是否在集合中,或者使用insert
函数插入一个新元素。
- 适用场景:
- 适用于需要存储一组唯一元素,并经常进行查找、插入和删除操作,同时对元素的顺序有要求的场景。比如,存储一个游戏中的玩家等级列表,每个等级只出现一次,并且可以方便地检查某个等级是否存在。
multiset
- 特点:
- 与
set
类似,但允许存储相同的元素,即元素可以不唯一。它的操作时间复杂度在平均情况下也为 。 - 例如,在一个存储学生成绩的
multiset
中,可以有多个学生得到相同的成绩。
- 适用场景:
- 适用于需要存储可能有重复元素的集合,并且需要进行排序和快速查找操作的场景。如统计一个班级学生的考试成绩分布,相同的成绩可能会有多个学生。
- 容器适配器(Container Adapters)
stack
(栈)
- 特点:
- 栈是一种后进先出(LIFO)的数据结构,它可以基于
vector
、list
或deque
来实现(默认通常是deque
)。主要操作有push
(入栈)、pop
(出栈)和top
(查看栈顶元素),push
和pop
操作的时间复杂度为 。 - 例如,在一个表达式求值的程序中,可以使用栈来存储操作数和运算符,按照运算顺序进行处理。
- 适用场景:
- 适用于需要实现后进先出操作的场景,如函数调用栈、表达式求值等。
queue
(队列)
- 特点:
- 队列是一种先进先出(FIFO)的数据结构,同样可以基于
vector
、list
或deque
实现(默认通常是deque
)。主要操作包括enqueue
(入队)、dequeue
(出队)和front
(查看队首元素),enqueue
和dequeue
操作的时间复杂度为 。 - 例如,在一个任务调度系统中,任务按照到达的顺序排队等待执行,可以用队列来存储这些任务。
- 适用场景:
- 适用于需要实现先进先出操作的场景,如任务排队、消息队列等。
priority_queue
(优先队列)
- 特点:
- 优先队列是一种特殊的队列,它的元素按照一定的优先级顺序出队,通常是基于堆(
heap
)实现的。默认情况下,最大元素(对于std::less
比较器)或者最小元素(对于std::greater
比较器)先出队,插入和删除操作的时间复杂度在平均情况下为 。 - 例如,在一个操作系统的进程调度中,可以根据进程的优先级将进程放入优先队列,优先级高的进程先执行。
- 适用场景:
- 适用于需要按照优先级处理元素的场景,如事件驱动系统中的事件处理,根据事件的紧急程度安排处理顺序。