vector
常用构造函数
// 构造容量为 100 的 vector,每个值都赋值为 256
// 如果不提供初始值,会填充对应元素类型的 默认值
std::vector<int> vec(100, 256);
// 范围构造:以 vec 容器中的前 50 个元素来构造一个 vector
// 任何 STL 容器都可以,数组也可以,如下面的例子
std::vector<int> vec2(vec.begin(), vec.begin() + 50);
// 以数组的值来构造 vec
int arr[] = {128, 123, 21, 12, 23, 321};
std::vector<int> vec2(arr, arr + sizeof(arr) / sizeof(int));
// 赋值构造函数
std::vector<int> vec2(vec);
添加元素
// 在 vector 的末尾添加元素 value
// 当添加的元素个数超过 vector 的容量时,会自动扩容,这可能会导致指向之前内容的指针无效
void push_back(const value_type& value)
// 和 push_back 功能一样,都是在 vector 的末尾位置添加元素
// 但是 emplace_back 没有复制开销
// 它会以 args 为参数原地调用 value_type 的构造函数来构造要添加的值
void emplace_back(Args&& ... args);
// 指定的位置 pos 处插入元素 val
// 返回新插入的元素的位置
iterator insert(const_iterator pos, const value_type& val);
// 指定的位置 pos 处插入范围 [first, last) 中的所有元素
// 返回新插入的第一个元素的位置
iterator insert(const_iterator pos, iterator first, iterator last);
// 在指定的位置 pos 处,以 args 为参数原理构造的方式添加一个元素
// 并返回新添加元素的位置
iterator emplace(const_iterator pos, Args&& ... args);
// assign 系列函数的作用是,使用新的元素替换容器中原有的元素,并相应的修改容器的大小。
// 用区间 [first, last) 中的元素替换容器中的元素
void assign(iterator first, iterator last);
// 用 n 个值为 val 的元素替换容器中原有的元素
void assign(size_t n, const value_type& val);
// 用初始化列表中的元素替换容器中的元素
void assign(intialiazer_list<value_type> li);
//-------------------------- 以下函数不常用 ------------------------------//
// 指定的位置 pos 处插入初始化元素列表中的所有元素
// 返回新插入的第一个元素的位置
iterator insert(const_iterator pos, intialiazer_list<value_type> li);
// 指定的位置 pos 处插入 n 个元素 val
// 返回新插入的第一个元素的位置
iterator insert(const_iterator pos, size_t n, const value_type& val);
需要注意的是,上面所有的添加元素的函数,都有可能发生自动扩容的情况,这可能导致你之前的指向 vector 某个元素的指针无效,从而应发程序崩溃等。
另外,因为 vector 底层的存储使用的是数组,所以最好是在 vector 的末尾添加元素,在中间位置或开头位置添加元素都会引发元素发生挪动,这是比较耗时的操作。
删除元素
// 移除容器中最后位置的元素 (被移除的元素会被析构)
void pop_back();
// 删除指定位置 pos 处的元素
// 返回被删除元素的下一个元素的位置
iterator erase(const_iterator pos);
// 删除指定区间: [first, last) 中的所有元素
// 返回被删除区间的下一个元素的位置
iterator erase(const_iterator first, const_iterator last);
// 移除容器中的所有元素 (调用每个元素的析构函数),调用该方法后,容器大小为 0.
// clear 不保证原 vector 的内存被重新分配,也不保证该表 vector 的容量大小。
void clear();
// 如果想要保证原 vector 的内存重新分配,可以调用 vector 的 swap 方法
vector<int>().clear(vec);
访问元素
// 返回 vector 容器开头位置的元素的引用
// 注意,若是 vector 为空,调用该函数是 UB 行为
reference front();
// 返回 vector 容器末尾位置的元素的引用
// 注意,若是 vector 为空,调用该函数是 UB 行为
reference back();
// 和 operator[] 功能类似,返回指定位置处的元素,
// 不同的是,at 函数会检查位置参数 n 是否合法,不合法的情况下会抛出 out_of_range 异常
reference at(size_t n);
// 返回内部用于存储 vector 的数组指针
value_type* data();
// 随机访问第 n 个元素的值
reference operator[](size_t n);
其他函数
// vector 的容量至少要为 n
// 如果当前容量小于 n,则重新分配至少可以容纳 n 个元素的空间;否则,函数无影响。
void reserve(size_t n);
// 重新调整 vector 容器的大小(注意不是容量)
// 如果 n 小于当前容器大小,则只保留前 n 个元素,其余被析构清除
// 如果 n 大于当前容器大小,则扩展容器元素大小到 n 个元素,默认值为元素的默认值
void resize(size_t n);
// 和上面的功能一致,只是扩展的情况下会填充指定的值 val
void resize(sizet_n, const value_type& val);
// 调整容器容量大小到容器 size 的大小,一般不用,除非特别在意空间
void shrink_to_fit();
// 下面是对 vector 迭代访问的一些函数
iterator begin();
iterator end();
const_iterator cbegin();
const_iterator cend();
iterator rbgin();
iterator rend();
const_iterator crbegin();
const_iterator crend();
queue
常用构造函数
// 构造一个空队列
std::queue<int> que;
// 以 cnt 的内容来构造队列 queue
// 底层默认是使用 std::deque 来保存队列元素,可以指定其他容器
explicit queue<T, container_type=deque<T>>(const container_type& ctn);
// 赋值构造函数
explicit queue(const queue& x);
std::deque<int> deq(3, 12);
// 构造一个队列,队列中的值是 deq 中的值
std::queue<int> que(deq);
std::vector<int> vec(5,256);
// 构造一个队列,队列中的值是 vec 中的值
std::queue<int,std::vecteor<int>> que(vec);
队列的增删查改
// 往队列尾部添加一个元素 val
void push(const value_type& val);
// 和push功能一样,都是往队列尾部添加一个元素 val
// 不同的是,emplace 会以原地构造的方式添加
void emplace(Args&& ... args);
// 删除队列的队头元素
void pop();
// 访问队列的队头元素
// 注意,这里返回的是引用,方便你直接修改队头元素
value_type& front();
// 队头元素乘 2
que.front() *= 2;
// 队列为空,返回true,否则返回false
bool empty() const;
// 返回队列的大小
size_t size() const;
// 交换两个队列
// 注意:交换的两个队列的底层容器必须一致,否则它们是两个不同的类型,无法调用该函数
void swap(queue& x) noexcept;
可以看到,队列的操作比较简单,没有其他的复杂操作,因为队列本身可以理解成是增删查改都受到一定限制的数组,所以,它的 push,pop ,emplace 等也都不需要带上 _back 等表示插入或删除位置的后缀。
stack
常用构造函数
// 以容器 cntr 的元素构造一个 stack 容器
// 默认的容器适配器类型时 std::deque
explicit stack(const container_type& ctnr=empty_container);
std::deque<int> mydeque(3,100);
// 以deque元素构造一个 stack
std::stack<int> first(mydeque);
// 以vector作为容器适配器构造一个 stack
std::stack<int,std::vector<int>> second(std::vector<int>{1,2,3});
std::stack<int> third; // 构造一个空的 stack
栈的增删查改
// 在栈顶位置添加一个元素
void push(const value_type& val);
// 以原地构造的方式在栈顶位置添加一个元素
void emplace(Args&&...args);
// 访问栈顶位置的元素,可以方便快速修改栈顶元素
reference top();
// 删除栈顶位置的元素
void pop();
// 返回栈的大小
size_t size();
// 交换两个栈的元素内容
void swap(stack& x);
可以看到,栈的操作比队列还要简单,那是因为栈的限制比队列还要打,因为栈的添加删除修改都只能在一端进行。
priority_queue
常用构造函数
// 构造一个优先级队列,内部比较算法是 comp,内容保存元素的容器是 cntr
// comp 默认是 std::less
// cntr 默认是 std::vector
priority_queue(const Compare& comp, const Container& cntr);
// 区间构造,和上一个一样,但是会插入 [first, last) 区间内的元素到优先级队列中
priority_queue(Iterator first, Iterator last, const Compare& comp, const Container& cntr);
// 构造一个空的优先级队列
// 默认比较函数是 less, 也就是大顶堆
// 默认使用的容器是 vector
std::priority_queue<int> que;
优先级队列的增删查改
// 往优先级队列中添加一个元素 val
void push(const value_type& val);
// 和push功能一样,都是往优先级队列中添加一个元素 val
// 不同的是,emplace 会以原地构造的方式添加
void emplace(Args&& ... args);
// 删除优先级队列的顶部元素,
void pop();
// 访问优先级队列的顶部元素
// 注意,这里返回的是常引用,不可修改
const value_type& top();
// 队列为空,返回true,否则返回false
bool empty() const;
// 返回队列的大小
size_t size() const;
// 交换两个队列
// 注意:交换的两个队列的底层容器必须一致,否则它们是两个不同的类型,无法调用该函数
void swap(queue& x) noexcept;
可以看到,priority_queue 的函数和 queue 几乎一样,只是队列的 front() 函数换成了 top() 函数,之所以会有这个差别,是因为所谓优先级队列其实和队列没有什么关系,它本质上是一个大/小顶堆。而叫它优先级队列也是因为它的性质和队列是一样的,不同的是,队列是以入队的顺序来出队的,而它是以元素的大小来出队的。
priority_queue 的内部实现其实就是调用的 std::make_heap, std::push_heap, std::pop_heap 这几个函数来实现的。
deque
deque 表示 double-ended-queue 的意思,也就是双端队列。它提供的功能其实和 vector 基本一致,但 vector 只支持在末尾进行高效的插入和删除操作;而 deque 同时支持在开头和末尾的位置进行高效的插入和删除操作。
deque 也支持对单个元素进行快速的随机访问,但是它不保证内部所有元素在内存中是连续存储的,所以通过偏移 deque 中某个元素的指针来访问另一个指针是属于 UB 行为。
常用构造函数
std::deque<int> deq;
// 构造一个有 n 个元素的 deque。
explicit deque(size_t n);
// 构造一个有 n 个元素,每个元素的值为 val 的 deque。
deque(size_t n, const value_type& val);
// 以区间 [first, last) 中的元素构造一个 deque。
deque(iteartor first, iterator last);
// 赋值构造
deque(const deque& x);
// 以初始化序列中的值构造 deque
deque(intializer_list<valye_type> li);
添加元素
// 在 deque 的末尾添加元素 value
void push_back(const value_type& value)
// 在 deque 的开头添加元素 value
void push_front (const value_type& value);
// 和 push_back 功能一样,都是在 deque 的末尾位置添加元素
// 但是 emplace_back 没有复制开销
// 它会以 args 为参数原地调用 value_type 的构造函数来构造要添加的值
void emplace_back(Args&& ... args);
// 和 push_front 功能一样,都是在 deque 的开头位置添加元素
// 但是 emplace_front 没有复制开销
// 它会以 args 为参数原地调用 value_type 的构造函数来构造要添加的值
void emplace_front (Args&&... args);
// 指定的位置 pos 处插入元素 val
// 返回新插入的元素的位置
iterator insert(const_iterator pos, const value_type& val);
// 指定的位置 pos 处插入范围 [first, last) 中的所有元素
// 返回新插入的第一个元素的位置
iterator insert(const_iterator pos, iterator first, iterator last);
// 在指定的位置 pos 处,以 args 为参数原理构造的方式添加一个元素
// 并返回新添加元素的位置
iterator emplace(const_iterator pos, Args&& ... args);
// assign 系列函数的作用是,使用新的元素替换容器中原有的元素,并相应的修改容器的大小。
// 用区间 [first, last) 中的元素替换容器中的元素
void assign(iterator first, iterator last);
// 用 n 个值为 val 的元素替换容器中原有的元素
void assign(size_t n, const value_type& val);
// 用初始化列表中的元素替换容器中的元素
void assign(intialiazer_list<value_type> li);
//-------------------------- 以下函数不常用 ------------------------------//
// 指定的位置 pos 处插入初始化元素列表中的所有元素
// 返回新插入的第一个元素的位置
iterator insert(const_iterator pos,
// 指定的位置 pos 处插入 n 个元素 val
// 返回新插入的第一个元素的位置
iterator insert(const_iterator pos, size_t n, const value_type& val);
删除元素
// 移除容器中最后一个位置的元素 (被移除的元素会被析构)
void pop_back();
// 移除容器中第一个位置的元素 (被移除的元素会被析构)
void pop_front();
// 删除指定位置 pos 处的元素
// 返回被删除元素的下一个元素的位置
iterator erase(const_iterator pos);
// 删除指定区间: [first, last) 中的所有元素
// 返回被删除区间的下一个元素的位置
iterator erase(const_iterator first, const_iterator last);
// 移除容器中的所有元素 (调用每个元素的析构函数),调用该方法后,容器大小为 0.
// clear 不保证原 vector 的内存被重新分配,也不保证该表 vector 的容量大小。
void clear();
// 如果想要保证原 vector 的内存重新分配,可以调用 vector 的 swap 方法
vector<int>().clear(vec);
访问元素
// 返回 vector 容器开头位置的元素的引用
// 注意,若是 vector 为空,调用该函数是 UB 行为
reference front();
// 返回 vector 容器末尾位置的元素的引用
// 注意,若是 vector 为空,调用该函数是 UB 行为
reference back();
// 和 operator[] 功能类似,返回指定位置处的元素,
// 不同的是,at 函数会检查位置参数 n 是否合法,不合法的情况下会抛出 out_of_range 异常
reference at(size_t n);
// 返回内部用于存储 vector 的数组指针
value_type* data();
// 随机访问第 n 个元素的值
reference operator[](size_t n);
其他函数
// 重新调整 vector 容器的大小(注意不是容量)
// 如果 n 小于当前容器大小,则只保留前 n 个元素,其余被析构清除
// 如果 n 大于当前容器大小,则扩展容器元素大小到 n 个元素,默认值为元素的默认值
void resize(size_t n);
// 和上面的功能一致,只是扩展的情况下会填充指定的值 val
void resize(sizet_n, const value_type& val);
// 调整容器容量大小到容器 size 的大小,一般不用,除非特别在意空间
void shrink_to_fit();
// 下面是对 vector 迭代访问的一些函数
iterator begin();
iterator end();
const_iterator cbegin();
const_iterator cend();
iterator rbgin();
iterator rend();
const_iterator crbegin();
const_iterator crend();
map、unordered_map
常用构造函数
// 默认构造函数,构造一个空的 map 容器
explicit map(const key_compare& comp=key_compare(), const alloc=aolloc);
// 区间构造函数,以区间 [first, last) 中的元素构造一个 map 容器
map(interator first, iterator last, const key_compare& comp=key_compare, alloc);
// 初始化列表的方式构造
map(initialize_list<valye_type> li, const key_compare& comp=key_compare, alloc);
// 赋值构造
map(const map& x);
可以看到 map 的每个构造函数基本都有一个对 key 进行比较的函数,你可以自定义 key 的比较规则,之所以需要这个比较函数是因为 map 中的元素是有序存储的。
// 默认构造函数,默认一个 0 大小的 unordered_map 容器
explicit unordered_map(size_t n=0, const hasher&=, const key_equal&=...);
// 区间构造,以区间 [first, last) 中的元素构造 unordered_map 容器
unordered_map(iterator first, iterator last, size_t n=0, const hasher...);
// 赋值构造
unordered_map(const unordered_map& x);
// 列表初始化构造
unordered_map(initializer_list<value_type> li, size_t n=0, const hasher...);
unordered_map 和 map 的功能一样,但是 map 是有序的, unordered_map 是无序的,所以,在访问单个元素的时候,unordered_map 的速度要比 map 更快。
添加元素
// 使用 args 参数原地构造一组 <key,value> 对元素并添加到 map 容器中。
// 如果插入成功,则返回新插入元素的迭代器和 true
// 否则,插入失败,表示对应的 key 已存在,返回对应等效元素的迭代器和 false
pair<iteraotr, bool> emplace(Args&& ... args);
// 插入值 val 到 map 容器中
pair<iterator, bool> insert(const value_type& val);
// 插入区间 [first, last) 中的元素到 map 容器中
void insert(iterator first, iterator last);
// 插入序列化中的元素到 map 容器中
void insert(initialize_list<value_type> li);
注意,map 中的 key 值是唯一的,上面所有的插入函数,当插入的 key 值已存在,是不会进行插入操作的。
删除元素
// 删除指定迭代器位置所在的元素,
// 返回被删除元素的下一个元素的迭代器
iterator erase(iterator pos);
// 删除指定的 key 的元素
// 返回被删除元素的个数
size_type erase(const key_type& k);
// 删除指定区间 [first, last) 中的元素
iterator erase(iterator first, iterator last);
访问元素
// 访问指定 key 值的元素值 value,注意是一个引用,可直接修改
// 当对应的 key 值不存在的时候,会抛异常
mapped_type& at(const key_type& k);
// 功能和 at 函数一样,但是
// 当对应的 key 值不存在的时候,会默认添加对应 key 的元素
mapped_type& operator[](const key_type& k);
// 返回容器中所有 key 值等于 k 的元素的一个范围的边界区间
// 但是因为 map 所有 key 都是唯一,所以这个区间中最大元素不会超过 1 个
pair<iterator,iterator> equal_range(const key_type& k);
其他函数
// 返回 key 值为 k 所对应 value 的个数
// 因为 key 值唯一,所以,该函数改么返回 1,要么返回 0;
// 作用就是用来判断容器中是否包含某个key值
size_t count(const key_type& k);
// 返回指定 key 值得元素所在的迭代器位置
// 如果对应的 key 值不存在,则返回 map::end 位置
iterator find(const key_type& k);
// 交换两个 map 容器的内容
void swap(map& x);
// 下面是对 vector 迭代访问的一些函数
iterator begin();
iterator end();
const_iterator cbegin();
const_iterator cend();
iterator rbgin();
iterator rend();
const_iterator crbegin();
const_iterator crend();
// 返回容器中 key 值第一个大于等于 k (对于 operator < 来说;反之则小于等于)的元素的迭代器
// 其实就是 k 为键值得元素的 下边界;是大于还是小于,得根据是从小到大排还是从大到小排。
iterator lower_bound(const key_type& k);
// 返回容器中 key 值第一个大 k(对于 operator < 来说;反之则小于) 的元素的迭代器
// 其实就是 k 为键值得元素的 上边界;
// 注意,和 lower_bound 相比,它没有等于
iterator upper_bound(const key_type& k);
从上面的描述可以看到,其实 lower_bound, upper_bound, 分别就是返回某个元素的左闭右开的一个区间。
set / unordered_set 其实就是没有 value 这个类型,或者说 value 就是它本身的 key 的一个容器。除此之外,它的内部原理以及提供的 API 函数和对应的 map / unordered_map 没有任何差别,这里不再赘述。