Interview_C++_day7
\(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\) 的底层原理
数据量大时使用快排来实现,然后分段递归。当数据量小于某个值后,为了避免递归带来的过大额外开销,使用插入排序。如果递归层次过深,就会改用堆排序。

                
            
        
浙公网安备 33010602011771号