STL
使用
常见的容器
序列式容器
string
结构 char* + size + capacity
扩容机制
不同编译器不同:
VS:1.5 倍扩容
GCC:2 倍扩容
- clear清空有效字符(不改变空间大小)
- resize(n,c),有效字符改为n个,扩大空间时并把多出空间置为c
- reserve(n)预留空间到n个,n<size时不改变空间大小
模拟实现
vector
结构
由三个迭代器组成(随机访问迭代器)
cpp
template<class T>
class vector
{
public:
// 迭代器:原生指针
typedef T* iterator;
private:
iterator _start; // 空间头
iterator _finish; // 有效数据尾
iterator _endofstorage;// 可用空间尾
};
扩容
- 倍数:VS1.5倍,gcc2倍
- 过程:单独申请一块连续空间,把原有数据拷贝到新空间,释放原有空间
- 扩容会导致迭代器失效:空间地址完全变了
list
迭代器(双向迭代器)
只能+±-,不能+n,-n,[]
每次插入或者删除一个元素,就配置或者释放一个空间,原有的迭代器不会失效
只需要一个指向node节点的指针即可
deque
迭代器(随机访问迭代器)
deque 是分段连续的双端队列,
包含
cpp
T* _cur; // 当前指向的元素
T* _first; // 当前缓冲区的头
T* _last; // 当前缓冲区的尾
T** _map; // 指向中控 map 的指针
底层是 分段连续的空间(不是一整块数组)
扩容高效,迭代器不会失效
关联式容器
map/set
- 底层
红黑树(平衡二叉搜索树) - 特点
key 有序(中序遍历有序)
key 不可重复
插入 / 删除 / 查找 O(logN)
迭代器:双向迭代器(++ / --) - 区别
set:只有 key
map:key + value(pair) - 迭代器失效
插入:不会失效
删除:只有被删节点迭代器失效
其他都有效
unordered_map/unordered_set
- 底层
哈希表(拉链法 / 开链法) - 特点
key 无序
key 不可重复
插入 / 删除 / 查找 O (1) 平均
迭代器:单向 / 向前迭代器 - 区别
unordered_set:只有 key
unordered_map:key + value - 迭代器失效
插入触发 rehash 扩容:全部迭代器失效
删除:只有被删迭代器失效
容器适配器
stack
底层用deque维护
queue
底层用deque维护
priority_queue
底层用vector维护
向上向下调整算法
迭代器
迭代器失效问题
序列式容器vector/deque
插入和删除元素之后,迭代器会失效
erase会返回下一个有效的迭代器
关联式容器map/set
删除后,当前元素的迭代器失效,只需要在删除前记录一下下一个元素的迭代器即可
为什么要有迭代器
借助迭代器,可以在不清楚对象内部表示的情况下,按照一种顺序访问每一个元素
迭代器不是指针,但是可以像指针一样 进行元素访问,是对于原生指针的封装
简单来说是可以在不暴露内部结构的前提下,循环遍历元素的效果
常见的算法
find
线性查找元素
find(起始迭代器, 结束迭代器, 目标值)
reverse
将迭代器范围内的元素原地反转。
reverse(起始迭代器, 结束迭代器)
sort
对迭代器范围内的元素原地排序,默认升序。
sort(起始迭代器, 结束迭代器, 比较函数/仿函数/lambda)
swap
交换两个对象或容器的内容。
swap(a, b)
lower_bound/upper_bound 二分查找(要求序列有序!)
lower_bound(起始迭代器, 结束迭代器, 目标值)
在升序序列中找第一个 ≥ 目标值的位置。
lower_bound(起始迭代器, 结束迭代器, 目标值)
在升序序列中找第一个 > 目标值的位置。
next_permutation
生成下一个字典序排列
存在下一个排列:返回 true,并修改序列为下一个排列。
不存在下一个排列(已是最后一个排列):返回 false,并将序列重置为第一个排列(升序)。
next_permutation(起始迭代器, 结束迭代器)
...
原理
六大组件
容器
多种数据结构,容器就是一种模板类
算法
算法是一种模板函数
迭代器
进行容器和算法之间的粘合剂,泛型指针,也是模板类
适配器
一种用来修饰容器或仿函数或迭代器接口的东西
仿函数
行为类似函数,可以作为算法的策略,重载了()
空间配置器
一个实现了动态空间配置管理释放的模板类