(map) 和 (set)
(map) 是映射,(set) 是集合,都是通过红黑树来实现的。他们的操作行为,都是转调红黑树的操作行为。
- (map) 中元素为 (键-值)key-value,关键字起到索引作用,值保存相关数据。(set) 元素为 (键)key,值即是键,
- (map) 允许修改 (value),不允许修改 (key),(set) 的迭代器是 (const) 的,不允许修改元素。原因: (map) 和 (set) 是通过关键字排序来实现有序的,如果要修改,需要删除原本键值,在插入新的键值,每次操作后都需要重新调节平衡,这样破坏了原本的结构,使得迭代器失效,不知道指向改变前的还是改变后的位置。
- (map) 支持下标操作,(set) 不支持。(map) 下标操作 ([key]) ,是通过关键字 (key) 去查找,返回该关键字的值,若该关键字不存在,则会插入一个具有该关键字和 (value) 类型默认值到 (map) 中。
(map) 和 (unordered\_map)
(map) | (unordered\_map) |
---|---|
通过红黑树实现 | 通过 (hash) 表实现 |
操作复杂度 (log) 级别 | 操作复杂度常数级别 |
内部有序 | 内部无序 |
适用于对顺序有要求的场景 | 适用于频繁查找的场景 |
(unordered\_map) 基于 (hash) 表,需要用 (vector) 来解决冲突,所以查找和存储的时间大大减少,而代价是花费更多内存。
(STL) 的组成
(STL) 由六部分组成:算法、容器、迭代器、仿函数、适配器、内存分配器。
- 通过迭代器,可以实现对容器内容的读和写。
- 对于重载了 (()) 的类,可以实现类似函数调用过程,叫做仿函数。
- 适配器将一个类的接口适配成用户指定的形式,使原本不兼容的类可以一起工作。
- 内存分配器负责空间的配置和管理。
容器和容器适配器
容器适配器是对容器的一种再封装,容器适配器不支持迭代。
(STL) 自带的容器有 (vector、list、map、set、deque),同时还提供了一些特别的容器适配器,比如 (stack、queue、priority\_queue)。
区别在于 (stack、queue) 的底层实现用到了 (deque),(priority\_queue) 的底层实现用到了 (vector),而 (vector) 的底层实现没有用到别的容器。
迭代器和指针
迭代器就是把不同集合类的访问逻辑抽象出来,使得不用暴露集合内部结构而达到遍历集合的效果。
迭代器不是指针,是类模板。迭代器只是模拟出指针的一些功能,本质是封装了原生指针,提供了比指针更高级的行为。迭代器返回的是对象的引用,而不是对象的值。
(vector) 和 (list)
- (vector) 存在扩容机制:
在向 (vector) 添加元素时,如果还有剩余的空间,那么会直接添加到指定位置,如果没有剩余的空间,那么会重新开辟原本容器的两倍空间,然后将旧的数据复制到新开辟的空间,释放旧内存。
vector | list | |
---|---|---|
类型 | 动态数组 | 动态链表 |
底层实现 | 数组实现 | 双向链表实现 |
访问 | 支持随机访问,(O(1)) | 不支持随机访问,(O(n)) |
插入 | 在末尾 (O(1)),在中间 (O(n)) | 很快,(O(1)) |
删除 | 在末尾 (O(1)),在中间 (O(n)) | 很快,(O(1)) |
内存来源 | 从堆区分配空间 | 从堆区分配空间 |
内存使用 | (vector) 是顺序内存 | (list) 不是顺序内存 |
内存分配 | (vector) 一次性分配好,不够时扩容 | (list) 每次插入节点都需要进行内存申请 |
性能 | (vector) 访问性能好,插入删除性能差 | (list) 插入删除性能好,访问性能差 |
适用场景 | 经常随机访问,不在乎插入和删除效率 | 经常插入删除,不在乎访问效率 |
(STL) 中 (resize) 和 (reserve)
(resize()) 改变当前容器内含有元素的数量。例如 (vector<int>g) 操作 (g.resize(n)),则容器新增 (n-g.size()) 个元素或删除末尾多出的元素。
(reserve()) 改变当前容器可存放元素的最大容量,例如操作 (g.reserve(n)),如果 (n) 值大于当前容器容量 (g.capacity()),则会重新分配一块能存 (n) 个对象的空间,然后把容器内元素复制过来并销毁之前的内存,否则不会发生变化。
(sort) 的底层原理
数据量大时使用快排来实现,然后分段递归。当数据量小于某个值后,为了避免递归带来的过大额外开销,使用插入排序。如果递归层次过深,就会改用堆排序。