STL
1. std::vector
底层原理:std::vector 是一个动态数组。它在内存中分配一块连续的存储空间来存储元素。随着元素数量的增加,如果存储空间不够用,vector 会分配一块更大的内存,并将现有元素复制到新内存块中。通常,vector 的容量会按一定比例(通常是两倍)扩展,以减少频繁的内存分配和复制操作。
优点:快速的随机访问(O(1))、尾部插入和删除操作(均摊 O(1))。
缺点:当容量需要扩展时,可能会发生较慢的重新分配和元素复制。
内部结构示意
template <typename T, typename Allocator = std::allocator<T>>
class vector {
private:
T* data; // 指向元素存储区域的指针
size_type size; // 当前存储的元素数量
size_type capacity; // 当前分配的最大存储容量
Allocator allocator; // 用于管理内存的分配器
// ...
};
2. std::list
底层原理:std::list 是一个双向链表。每个元素都存储在一个节点中,节点包含数据和指向前后节点的指针。链表的元素在内存中不需要是连续的,因此插入和删除元素非常高效(O(1)),但不支持快速随机访问(O(n))。
优点:高效的插入和删除操作,尤其是在中间位置。
缺点:较慢的随机访问,不适合需要频繁索引访问的场景。
内部结构示意
template<typename T>
struct Node {
T data; // 节点存储的数据
Node* prev; // 指向前一个节点的指针
Node* next; // 指向后一个节点的指针
};
template<typename T>
class list {
private:
Node<T>* head; // 指向链表的头节点
Node<T>* tail; // 指向链表的尾节点
size_t size; // 链表中的元素数量
// 其他成员...
};
3. std::deque
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据。
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间。
底层原理:std::deque(双端队列)实现为一组连续的小块内存(通常称为块或缓冲区),这些块通过指针数组连接在一起。deque 支持双端操作,因此可以在两端高效地插入和删除元素。随机访问的时间复杂度为 O(1)。
优点:快速的双端插入和删除、快速的随机访问。
缺点:相比 vector,内存占用略高,因为需要额外的指针来管理块。
内部结构示意
template <typename T, typename Allocator = std::allocator<T>>
class deque {
private:
typedef T* pointer; // 定义指向元素的指针类型
pointer* map; // 内存块数组(map),存储指向每个内存块的指针
// 头部和尾部指针
pointer start;
pointer finish;
size_t map_size; // 当前容量和块的大小
static const size_t block_size = 512; // 内存块大小
Allocator allocator; // 分配器,用于分配内存
}
4. std::stack
底层原理:std::stack 是一种适配器容器,通常基于 std::deque 或 std::vector 实现。它提供了一种后进先出(LIFO)的数据结构,支持 push、pop、top 等操作。
优点:简单的 LIFO 操作,底层实现可以灵活选择。
缺点:只提供有限的接口,无法遍历或随机访问。
5. std::queue
底层原理:std::queue 是一种适配器容器,通常基于 std::deque 实现。它提供了一种先进先出(FIFO)的数据结构,支持 push(入队)、pop(出队)、front(获取队头)等操作。
优点:简单的 FIFO 操作,底层实现可以灵活选择。
缺点:只提供有限的接口,无法遍历或随机访问。
6. std::priority_queue
底层原理:std::priority_queue 通常使用 std::vector 作为底层容器,并基于二叉堆(通常是最大堆)实现。它允许你高效地访问并删除优先级最高的元素(通常是最大元素)。
优点:支持高效的最大或最小值访问,插入和删除操作的时间复杂度为 O(log n)。
缺点:随机访问和遍历效率低,不适合频繁需要全部元素排序的场景。
7. std::set 和 std::multiset
底层原理:std::set 和 std::multiset 通常基于红黑树实现。红黑树是一种自平衡二叉搜索树,能够保证基本操作(插入、删除、查找)的时间复杂度为 O(log n)。std::set 存储唯一元素,而 std::multiset 允许重复元素。
优点:高效的查找、插入和删除操作,元素自动排序。
缺点:相比于 unordered_set,性能稍差,内存占用稍高。
8. std::map 和 std::multimap
底层原理:std::map 和 std::multimap 也基于红黑树实现。它们存储的是键值对(key-value),std::map 中的键是唯一的,而 std::multimap 允许相同的键出现多次。
优点:高效的键查找、插入和删除操作,键值对按键自动排序。
缺点:相比于 unordered_map,查找和插入性能稍差,内存占用稍高。
部分内部结构示意
template<typename Key, typename T>
struct Node {
std::pair<const Key, T> data; // 键值对
Node* parent; // 指向父节点的指针
Node* left; // 指向左子节点的指针
Node* right; // 指向右子节点的指针
bool color; // 节点颜色(红或黑)
};
9. std::unordered_set 和 std::unordered_map
底层原理:std::unordered_set 和 std::unordered_map 基于哈希表实现。哈希表使用哈希函数将键映射到桶中,从而实现常数时间复杂度(O(1))的查找、插入和删除操作。
优点:非常高效的查找、插入和删除操作,适合不需要排序的场景。
缺点:键的顺序不固定,容易受到哈希冲突的影响,性能可能不如红黑树稳定。
10. std::unordered_multiset 和 std::unordered_multimap
底层原理:与 std::unordered_set 和 std::unordered_map 类似,但允许存储重复的元素(unordered_multiset)或重复的键(unordered_multimap)。
优点:与 unordered_set 和 unordered_map 类似,提供高效的查找、插入和删除操作,允许重复元素或键。
缺点:键的顺序不固定,容易受到哈希冲突的影响。
vector 动态数组,list 双向链表,deque 双端队列,stack 栈, queue 队列, map 红黑树(键值对), set 红黑树(不允许元素重复)