全网最全面的STL总结与常见面试题,值得收藏(三)

简介: 全网最全面的STL总结与常见面试题,值得收藏

priority_queue


优先队列,其底层是用堆来实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。


set


set 是按照特定顺序存储唯一元素的容器。


template<class _Kty,
    class _Pr = less<_Kty>,
    class _Alloc = allocator<_Kty> >
class set

set 的 底层数据结构是 红黑树,一种高效的平衡检索二叉树。


set 容器中 每一个元素就是二叉树的每一个节点,对于set容器的插入删除操作,效率都比较高,原因是因为二叉树的删除插入元素并不需要进行内存拷贝和内存移动,只是改变了指针的指向。


对 set 进行插入删除操作 都不会引起iterator的失效,因为迭代器相当于一个指针指向每一个二叉树的节点,对set的插入删除并不会改变原有内存中节点的改变, 但是vector的插入删除操作一般会发生内存移动和内存拷贝,所以会发生迭代器的失效。


set容器的检索速度很快,因为采用二分查找的方法 。


multiset


multiset允许元素重复而set不允许。


template<class _Kty,
  class _Pr = less<_Kty>,
  class _Alloc = allocator<_Kty> >
class multiset


map


map 是关联容器,按照特定顺序存储由 key value (键值) 和 mapped value (映射值) 组合形成的元素。


由于 RB-tree 是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL map 即以 RB-tree 为底层机制。又由于 map 所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 map 操作行为,都只是转调 RB-tree 的操作行为。


方法 说明

map 构造函数

begin 返回引用容器中第一个元素的迭代器

key_comp 返回容器用于比较键的比较对象的副本

value_comp 返回可用于比较两个元素的比较对象,以获取第一个元素的键是否在第二个元素之前

find 在容器中搜索具有等于 k的键的元素,如果找到返回一个迭代器,否则返回 map::end

count 在容器中搜索具有等于 k(参数)的键的元素,并返回匹配的数量

lower_bound 返回一个非递减序列 [first, last)中的第一个大于等于值 val的位置的迭代器

upper_bound 返回一个非递减序列 [first, last)中第一个大于 val的位置的迭代器

equal_range 获取相同元素的范围,返回包含容器中所有具有与 k等价的键的元素的范围边界

multimap


multimap 的特性以及用法与 map 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制 RB-tree 的 insert_equal() 而非 insert_unique。


unordered_set


unordered_set是基于哈希表,因此要了解unordered_set,就必须了解哈希表的机制。哈希表是根据关键码值而进行直接访问的数据结构,通过相应的哈希函数(也称散列函数)处理关键字得到相应的关键码值,关键码值对应着一个特定位置,用该位置来存取相应的信息,这样就能以较快的速度获取关键字的信息。


template < class Key,  
    class Hash = hash<Key>,  
    class Pred = equal_to<Key>,  
    class Alloc = allocator<Key>  
> class unordered_set;

4 算法


简单查找算法,要求输入迭代器(input iterator)


find(beg, end, val); // 返回一个迭代器,指向输入序列中第一个等于 val 的元素,未找到返回 end

find_if(beg, end, unaryPred); // 返回一个迭代器,指向第一个满足 unaryPred 的元素,未找到返回 end

find_if_not(beg, end, unaryPred); // 返回一个迭代器,指向第一个令 unaryPred 为 false 的元素,未找到返回 end

count(beg, end, val); // 返回一个计数器,指出 val 出现了多少次

count_if(beg, end, unaryPred); // 统计有多少个元素满足 unaryPred

all_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否所有元素都满足 unaryPred

any_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否任意(存在)一个元素满足 unaryPred

none_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否所有元素都不满足 unaryPred

查找重复值的算法,传入向前迭代器(forward iterator)


adjacent_find(beg, end); // 返回指向第一对相邻重复元素的迭代器,无相邻元素则返回 end

adjacent_find(beg, end, binaryPred); // 返回指向第一对相邻重复元素的迭代器,无相邻元素则返回 end

search_n(beg, end, count, val); // 返回一个迭代器,从此位置开始有 count 个相等元素,不存在则返回 end

search_n(beg, end, count, val, binaryPred); // 返回一个迭代器,从此位置开始有 count 个相等元素,不存在则返回 end

查找子序列算法,除 find_first_of(前两个输入迭代器,后两个前向迭代器) 外,都要求两个前向迭代器


search(beg1, end1, beg2, end2); // 返回第二个输入范围(子序列)在爹一个输入范围中第一次出现的位置,未找到则返回 end1

search(beg1, end1, beg2, end2, binaryPred); // 返回第二个输入范围(子序列)在爹一个输入范围中第一次出现的位置,未找到则返回 end1

find_first_of(beg1, end1, beg2, end2); // 返回一个迭代器,指向第二个输入范围中任意元素在第一个范围中首次出现的位置,未找到则返回end1

find_first_of(beg1, end1, beg2, end2, binaryPred); // 返回一个迭代器,指向第二个输入范围中任意元素在第一个范围中首次出现的位置,未找到则返回end1

find_end(beg1, end1, beg2, end2); // 类似 search,但返回的最后一次出现的位置。如果第二个输入范围为空,或者在第一个输入范围为空,或者在第一个输入范围中未找到它,则返回 end1

find_end(beg1, end1, beg2, end2, binaryPred); // 类似 search,但返回的最后一次出现的位置。如果第二个输入范围为空,或者在第一个输入范围为空,或者在第一个输入范围中未找到它,则返回 end1

其他只读算法,传入输入迭代器


for_each(beg, end, unaryOp); // 对输入序列中的每个元素应用可调用对象 unaryOp,unaryOp 的返回值被忽略

mismatch(beg1, end1, beg2); // 比较两个序列中的元素。返回一个迭代器的 pair,表示两个序列中第一个不匹配的元素

mismatch(beg1, end1, beg2, binaryPred); // 比较两个序列中的元素。返回一个迭代器的 pair,表示两个序列中第一个不匹配的元素

equal(beg1, end1, beg2); // 比较每个元素,确定两个序列是否相等。

equal(beg1, end1, beg2, binaryPred); // 比较每个元素,确定两个序列是否相等。

二分搜索算法,传入前向迭代器或随机访问迭代器(random-access iterator),要求序列中的元素已经是有序的


lower_bound(beg, end, val); // 返回一个非递减序列 [beg, end) 中的第一个大于等于值 val 的位置的迭代器,不存在则返回 end

lower_bound(beg, end, val, comp); // 返回一个非递减序列 [beg, end) 中的第一个大于等于值 val 的位置的迭代器,不存在则返回 end

upper_bound(beg, end, val); // 返回一个非递减序列 [beg, end) 中第一个大于 val 的位置的迭代器,不存在则返回 end

upper_bound(beg, end, val, comp); // 返回一个非递减序列 [beg, end) 中第一个大于 val 的位置的迭代器,不存在则返回 end

equal_range(beg, end, val); // 返回一个 pair,其 first 成员是 lower_bound 返回的迭代器,其 second 成员是 upper_bound 返回的迭代器

binary_search(beg, end, val); // 返回一个 bool 值,指出序列中是否包含等于 val 的元素。对于两个值 x 和 y,当 x 不小于 y 且 y 也不小于 x 时,认为它们相等。

只写不读算法,要求输出迭代器(output iterator)


fill(beg, end, val); // 将 val 赋予每个元素,返回 void

fill_n(beg, cnt, val); // 将 val 赋予 cnt 个元素,返回指向写入到输出序列最有一个元素之后位置的迭代器

genetate(beg, end, Gen); // 每次调用 Gen() 生成不同的值赋予每个序列,返回 void

genetate_n(beg, cnt, Gen); // 每次调用 Gen() 生成不同的值赋予 cnt 个序列,返回指向写入到输出序列最有一个元素之后位置的迭代器


7.使用输入迭代器的写算法,读取一个输入序列,将值写入到一个输出序列(dest)中


copy(beg, end, dest); // 从输入范围将元素拷贝所有元素到 dest 指定定的目的序列

copy_if(beg, end, dest, unaryPred); // 从输入范围将元素拷贝满足 unaryPred 的元素到 dest 指定定的目的序列

copy_n(beg, n, dest); // 从输入范围将元素拷贝前 n 个元素到 dest 指定定的目的序列

move(beg, end, dest); // 对输入序列中的每个元素调用 std::move,将其移动到迭代器 dest 开始始的序列中

transform(beg, end, dest, unaryOp); // 调用给定操作(一元操作),并将结果写到dest中

transform(beg, end, beg2, dest, binaryOp); // 调用给定操作(二元操作),并将结果写到dest中

replace_copy(beg, end, dest, old_val, new_val); // 将每个元素拷贝到 dest,将等于 old_val 的的元素替换为 new_val

replace_copy_if(beg, end, dest, unaryPred, new_val); // 将每个元素拷贝到 dest,将满足 unaryPred 的的元素替换为 new_val

merge(beg1, end1, beg2, end2, dest); // 两个输入序列必须都是有序的,用小于号运算符将合并后的序列写入到 dest 中

merge(beg1, end1, beg2, end2, dest, comp); // 两个输入序列必须都是有序的,使用给定的比较操作(comp)将合并后的序列写入到 dest 中

8.划分算法,要求双向选代器(bidirectional iterator)


is_partitioned(beg, end, unaryPred); // 如果所有满足谓词 unaryPred 的元素都在不满足 unarypred 的元素之前,则返回 true。若序列为空,也返回 true

partition_copy(beg, end, dest1, dest2, unaryPred); // 将满足 unaryPred 的元素拷贝到到 dest1,并将不满足 unaryPred 的元素拷贝到到 dest2。返回一个迭代器 pair,其 first 成员表示拷贝到 dest1 的的元素的末尾,second 表示拷贝到 dest2 的元素的末尾。

partitioned_point(beg, end, unaryPred); // 输入序列必须是已经用 unaryPred 划分过的。返回满足  unaryPred 的范围的尾后迭代器。如果返回的迭代器不是 end,则它指向的元素及其后的元素必须都不满足 unaryPred

stable_partition(beg, end, unaryPred); // 使用 unaryPred 划分输入序列。满足 unaryPred 的元素放置在序列开始,不满足的元素放在序列尾部。返回一个迭代器,指向最后一个满足 unaryPred 的元素之后的位置如果所有元素都不满足 unaryPred,则返回 beg

partition(beg, end, unaryPred); // 使用 unaryPred 划分输入序列。满足 unaryPred 的元素放置在序列开始,不满足的元素放在序列尾部。返回一个迭代器,指向最后一个满足 unaryPred 的元素之后的位置如果所有元素都不满足 unaryPred,则返回 beg

排序算法,要求随机访问迭代器(random-access iterator)


sort(beg, end); // 排序整个范围
stable_sort(beg, end); // 排序整个范围(稳定排序)
sort(beg, end, comp); // 排序整个范围
stable_sort(beg, end, comp); // 排序整个范围(稳定排序)
is_sorted(beg, end); // 返回一个 bool 值,指出整个输入序列是否有序
is_sorted(beg, end, comp); // 返回一个 bool 值,指出整个输入序列是否有序
is_sorted_until(beg, end); // 在输入序列中査找最长初始有序子序列,并返回子序列的尾后迭代器
is_sorted_until(beg, end, comp); // 在输入序列中査找最长初始有序子序列,并返回子序列的尾后迭代器
partial_sort(beg, mid, end); // 排序 mid-beg 个元素。即,如果 mid-beg 等于 42,则此函数将值最小的 42 个元素有序放在序列前 42 个位置
partial_sort(beg, mid, end, comp); // 排序 mid-beg 个元素。即,如果 mid-beg 等于 42,则此函数将值最小的 42 个元素有序放在序列前 42 个位置
partial_sort_copy(beg, end, destBeg, destEnd); // 排序输入范围中的元素,并将足够多的已排序元素放到 destBeg 和 destEnd 所指示的序列中
partial_sort_copy(beg, end, destBeg, destEnd, comp); // 排序输入范围中的元素,并将足够多的已排序元素放到 destBeg 和 destEnd 所指示的序列中
nth_element(beg, nth, end); // nth 是一个迭代器,指向输入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之后的元素都大于等于它
nth_element(beg, nth, end, comp); // nth 是一个迭代器,指向输入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之后的元素都大于等于它
使用前向迭代器的重排算法。普通版本在输入序列自身内部重拍元素,_copy 版本完成重拍后写入到指定目的序列中,而不改变输入序列
remove(beg, end, val); // 通过用保留的元素覆盖要删除的元素实现删除 ==val 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_if(beg, end, unaryPred); // 通过用保留的元素覆盖要删除的元素实现删除满足 unaryPred 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_copy(beg, end, dest, val); // 通过用保留的元素覆盖要删除的元素实现删除 ==val 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_copy_if(beg, end, dest, unaryPred); // 通过用保留的元素覆盖要删除的元素实现删除满足 unaryPred 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
unique(beg, end); // 通过对覆盖相邻的重复元素(用 == 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique (beg, end, binaryPred); // 通过对覆盖相邻的重复元素(用 binaryPred 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique_copy(beg, end, dest); // 通过对覆盖相邻的重复元素(用 == 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique_copy_if(beg, end, dest, binaryPred); // 通过对覆盖相邻的重复元素(用 binaryPred 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
rotate(beg, mid, end); // 围绕 mid 指向的元素进行元素转动。元素 mid 成为为首元素,随后是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。返回一个迭代器,指向原来在 beg 位置的元素
rotate_copy(beg, mid, end, dest); // 围绕 mid 指向的元素进行元素转动。元素 mid 成为为首元素,随后是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。返回一个迭代器,指向原来在 beg 位置的元素
使用双向迭代器的重排算法
reverse(beg, end); // 翻转序列中的元素,返回 void
reverse_copy(beg, end, dest);; // 翻转序列中的元素,返回一个迭代器,指向拷贝到目的序列的元素的尾后位置
使用随机访问迭代器的重排算法
random_shuffle(beg, end); // 混洗输入序列中的元素,返回 void
random_shuffle(beg, end, rand); // 混洗输入序列中的元素,rand 接受一个正整数的随机对象,返回 void
shuffle(beg, end, Uniform_rand); // 混洗输入序列中的元素,Uniform_rand 必须满足均匀分布随机数生成器的要求,返回 void

最小值和最大值


min(val1, va12); // 返回 val1 和 val2 中的最小值,两个实参的类型必须完全一致。参数和返回类型都是 const的引引用,意味着对象不会被拷贝。下略
min(val1, val2, comp);
min(init_list);
min(init_list, comp);
max(val1, val2);
max(val1, val2, comp);
max(init_list);
max(init_list, comp);
minmax(val1, val2); // 返回一个 pair,其 first 成员为提供的值中的较小者,second 成员为较大者。下略
minmax(vall, val2, comp);
minmax(init_list);
minmax(init_list, comp);
min_element(beg, end); // 返回指向输入序列中最小元素的迭代器
min_element(beg, end, comp); // 返回指向输入序列中最小元素的迭代器
max_element(beg, end); // 返回指向输入序列中最大元素的迭代器
max_element(beg, end, comp); // 返回指向输入序列中最大元素的迭代器
minmax_element(beg, end); // 返回一个 pair,其中 first 成员为最小元素,second 成员为最大元素
minmax_element(beg, end, comp); // 返回一个 pair,其中 first 成员为最小元素,second 成员为最大元素
字典序比较,根据第一对不相等的元素的相对大小来返回结果。如果第一个序列在字典序中小于第二个序列,则返回 true。否则,返回 fa1se。如果个序列比另一个短,且所有元素都与较长序列的对应元素相等,则较短序列在字典序中更小。如果序列长度相等,且对应元素都相等,则在字典序中任何一个都不大于另外一个。
lexicographical_compare(beg1, end1, beg2, end2);
lexicographical_compare(beg1, end1, beg2, end2, comp);


5 如何选择合适的容器


需要根据容器的特点和使用场景而定,可能满足需求的不止一种容器。


按是否有序关联性分为:


序列式容器:array、vector、deque、list 和 forward_list;


关联式容器:map、multimap、set 和 multiset;


无序关联式容器:unordered_map、unordered_multimap、unordered_set 和 unordered_multiset;


容器适配器:stack、queue 和 priority_queue。 根据容器底层采用是否是连续的存储空间分为:


采用连续的存储空间:array、vector、deque;


采用分散的存储空间:list、forward_list 以及所有的关联式容器和哈希容器。


注意:deque 容器归为使用连续存储空间的这一类,是存在争议的。因为 deque 容器底层采用一段一段的连续空间存储元素,但是各段存储空间之间并不一定是紧挨着的。



image.png

选择容器流程图(来源于网络)


选择容器的几点建议:


如果只是存储确定或不确定的对象,而不去删除它们,可以选用vector。就是因为vector是数组的替代品,是连续内存的,不适合频繁的删除。


如果在容器的指定位置插入新元素,则只能选择序列式容器,不选择关联式容器和哈希容器。


如果频繁的插入和删除,可以选用list(链表),内存不是连续的,可以方便的插入和删除,但是不支持索引访问。


数据量很大,不在乎他们的排序,要求效率,对容器中各元素的存储位置没有要求,可以考虑使用哈希容器,反之就要避免使用哈希容器。


如果是随机访问迭代器,选择 array、vector、deque。


如果是双向迭代器,考虑 list 序列式容器以及所有的关联式容器。


如果必须是前向迭代器,考虑 forward_list序列式容器以及所有的哈希容器。


如果插入或删除操作时,容器中的其它元素不移动?选择不是array、vector、deque的其它容器。


6 面试中常出现的STL问题


vector的底层原理


vector底层是一个动态数组,包含三个迭代器,start和finish之间是已经被使用的空间范围,end_of_storage是整块连续空间包括备用空间的尾部。


当空间不够装下数据(vec.push_back(val))时,会自动申请另一片更大的空间(1.5倍或者2倍),然后把原来的数据拷贝到新的内存空间,接着释放原来的那片空间【vector内存增长机制】。


当释放或者删除(vec.clear())里面的数据时,其存储空间不释放,仅仅是清空了里面的数据。


因此,对vector的任何操作一旦引起了空间的重新配置,指向原vector的所有迭代器会都失效了


image.png


vector中的reserve和resize的区别


reserve是直接扩充到已经确定的大小,可以减少多次开辟、释放空间的问题(优化push_back),就可以提高效率,其次还可以减少多次要拷贝数据的问题。reserve只是保证vector中的空间大小(capacity)最少达到参数所指定的大小n。reserve()只有一个参数。


resize()可以改变有效空间的大小,也有改变默认值的功能。capacity的大小也会随着改变。resize()可以有多个参数。


vector中的size和capacity的区别


size表示当前vector中有多少个元素(finish - start);


capacity函数则表示它已经分配的内存中可以容纳多少元素(end_of_storage - start);


vector中erase方法与algorithn中的remove方法区别


vector中erase方法真正删除了元素,迭代器不能访问了


remove只是简单地将元素移到了容器的最后面,迭代器还是可以访问到。因为algorithm通过迭代器进行操作,不知道容器的内部结构,所以无法进行真正的删除。


vector迭代器失效的情况


当插入一个元素到vector中,由于引起了内存重新分配,所以指向原内存的迭代器全部失效。


当删除容器中一个元素后,该迭代器所指向的元素已经被删除,那么也造成迭代器失效。erase方法会返回下一个有效的迭代器,所以当我们要删除某个元素时,需要it=vec.erase(it);。


正确释放vector的内存(clear(), swap(), shrink_to_fit())


vec.clear():清空内容,但是不释放内存。


vector().swap(vec):清空内容,且释放内存,想得到一个全新的vector。


vec.shrink_to_fit():请求容器降低其capacity和size匹配。


vec.clear();vec.shrink_to_fit();:清空内容,且释放内存。


list的底层原理


image.png


ist的底层是一个双向链表,使用链表存储数据,并不会将它们存储到一整块连续的内存空间中。恰恰相反,各元素占用的存储空间(又称为节点)是独立的、分散的,它们之间的线性关系通过指针来维持,每次插入或删除一个元素,就配置或释放一个元素空间。


list不支持随机存取,如果需要大量的插入和删除,而不关心随即存取


什么情况下用vector,什么情况下用list,什么情况下用deque


vector可以随机存储元素(即可以通过公式直接计算出元素地址,而不需要挨个查找),但在非尾部插入删除数据时,效率很低,适合对象简单,对象数量变化不大,随机访问频繁。除非必要,我们尽可能选择使用vector而非deque,因为deque的迭代器比vector迭代器复杂很多。


list不支持随机存储,适用于对象大,对象数量变化频繁,插入和删除频繁,比如写多读少的场景。


需要从首尾两端进行插入或删除操作的时候需要选择deque。


priority_queue的底层原理


priority_queue:优先队列,其底层是用堆来实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。


map 、set、multiset、multimap的底层原理


map 、set、multiset、multimap的底层实现都是红黑树,epoll模型的底层数据结构也是红黑树,linux系统中CFS进程调度算法,也用到红黑树。


image.png


红黑树的特性:


每个结点或是红色或是黑色;


根结点是黑色;


每个叶结点是黑的;


如果一个结点是红的,则它的两个儿子均是黑色;


每个结点到其子孙结点的所有路径上包含相同数目的黑色结点。


为何map和set的插入删除效率比其他序列容器高


因为不需要内存拷贝和内存移动


为何map和set每次Insert之后,以前保存的iterator不会失效?


因为插入操作只是结点指针换来换去,结点内存没有改变。而iterator就像指向结点的指针,内存没变,指向内存的指针也不会变。


当数据元素增多时(从10000到20000),map的set的查找速度会怎样变化?


RB-TREE用二分查找法,时间复杂度为logn,所以从10000增到20000时,查找次数从log10000=14次到log20000=15次,多了1次而已。


map 、set、multiset、multimap的特点


set和multiset会根据特定的排序准则自动将元素排序,set中元素不允许重复,multiset可以重复。


map和multimap将key和value组成的pair作为元素,根据key的排序准则自动将元素排序(因为红黑树也是二叉搜索树,所以map默认是按key排序的),map中元素的key不允许重复,multimap可以重复。


map和set的增删改查速度为都是logn,是比较高效的。


为何map和set的插入删除效率比其他序列容器高,而且每次insert之后,以前保存的iterator不会失效?


存储的是结点,不需要内存拷贝和内存移动。


插入操作只是结点指针换来换去,结点内存没有改变。而iterator就像指向结点的指针,内存没变,指向内存的指针也不会变。


为何map和set不能像vector一样有个reserve函数来预分配数据?


在map和set内部存储的已经不是元素本身了,而是包含元素的结点。也就是说map内部使用的Alloc并不是map声明的时候从参数中传入的Alloc。


set的底层实现实现为什么不用哈希表而使用红黑树?


set中元素是经过排序的,红黑树也是有序的,哈希是无序的


如果只是单纯的查找元素的话,那么肯定要选哈希表了,因为哈希表在的最好查找时间复杂度为O(1),并且如果用到set中那么查找时间复杂度的一直是O(1),因为set中是不允许有元素重复的。而红黑树的查找时间复杂度为O(lgn)


hash_map与map的区别?什么时候用hash_map,什么时候用map? 构造函数:hash_map需要hash function和等于函数,而map需要比较函数(大于或小于)。


存储结构:hash_map以hashtable为底层,而map以RB-TREE为底层。


总的说来,hash_map查找速度比map快,而且查找速度基本和数据量大小无关,属于常数级别。而map的查找速度是logn级别。但不一定常数就比log小,而且hash_map还有hash function耗时。


如果考虑效率,特别当元素达到一定数量级时,用hash_map。


考虑内存,或者元素数量较少时,用map。


迭代器失效的问题


插入操作:


对于vector和string,如果容器内存被重新分配,iterators,pointers,references失效;如果没有重新分配,那么插入点之前的iterator有效,插入点之后的iterator失效;


对于deque,如果插入点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,deque的迭代器失效,但reference和pointers有效;


对于list和forward_list,所有的iterator,pointer和refercnce有效。


删除操作:


对于vector和string,删除点之前的iterators,pointers,references有效;off-the-end迭代器总是失效的;


对于deque,如果删除点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,off-the-end失效,其他的iterators,pointers,references有效;


对于list和forward_list,所有的iterator,pointer和refercnce有效。


对于关联容器map来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用,否则会导致程序无定义的行为。


线程不安全的情况


在对同一个容器进行多线程的读写、写操作时;


在每次调用容器的成员函数期间都要锁定该容器;


在每个容器返回的迭代器(例如通过调用begin或end)的生存期之内都要锁定该容器;


在每个在容器上调用的算法执行期间锁定该容器。


参考资料


http://www.cplusplus.com/reference/stl/https://blog.csdn.net/qq_23350817/article/details/87930715https://blog.csdn.net/bizhu12/article/details/6769976             http://c.biancheng.net/view/7385.html

https://blog.csdn.net/daaikuaichuan/article/details/80717222


相关文章
|
6月前
|
存储 算法 C++
C/C++工程师面试题(STL篇)
C/C++工程师面试题(STL篇)
132 6
|
1月前
|
算法 安全 Linux
【C++STL简介】——我与C++的不解之缘(八)
【C++STL简介】——我与C++的不解之缘(八)
|
6月前
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(6) 常用集合算法
黑马c++ STL常用算法 笔记(6) 常用集合算法
|
6月前
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(1) 遍历算法
黑马c++ STL常用算法 笔记(1) 遍历算法
|
6月前
|
设计模式 C语言 C++
【C++进阶(六)】STL大法--栈和队列深度剖析&优先级队列&适配器原理
【C++进阶(六)】STL大法--栈和队列深度剖析&优先级队列&适配器原理
|
6月前
|
存储 算法
【数据结构与算法】【腾讯阿里链表面试题】算法题--链表易懂版讲解
【数据结构与算法】【腾讯阿里链表面试题】算法题--链表易懂版讲解
|
6月前
|
算法 程序员
腾讯T4纯手打《数据结构和算法》源码笔记,学完一脚踢进大厂
经历过互联网公司面试的同学大概都知道,数据结构和算法的知识技术栈是不可避免的,并且在笔试中,最重要的是靠算法题,尤其像头条这种大厂公司,上来就是算法题,答不出来的基本面试机会也不会有了。
|
存储 缓存 安全
杰哥教你面试之一百问系列:java集合
集合是我们在java中经常会用到的东西,熟悉了集合我们就熟悉了java。当面试官在Java面试中涉及到Java集合的问题时,通常会涉及到集合的概念、类型、常见操作、性能等方面的内容。
|
存储 算法 C++
C++学习之路—— STL标准模板库概述
C++学习之路—— STL标准模板库概述
C++学习之路—— STL标准模板库概述