跳转至

STL

vector如何扩展内存和释放内存

各种容器对比

容器 底层数据结构 时间复杂度 有无序 可不可重复 其他
array 数组 随机读改 O(1) 无序 可重复 支持快速随机访问
vector 数组 随机读改、尾部插入、尾部删除 O(1)、头部插入、头部删除 O(n) 无序 可重复 支持快速随机访问
list 双向链表 插入、删除 O(1)、随机读改 O(n) 无序 可重复 支持快速增删
deque 双端队列 头尾插入、头尾删除 O(1) 无序 可重复 一个中央控制器 + 多个缓冲区,支持首尾快速增删,支持随机访问
stack deque / list 顶部插入、顶部删除 O(1) 无序 可重复 deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时
queue deque / list 尾部插入、头部删除 O(1) 无序 可重复 deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时
priority_queue vector + max - heap 插入、删除 O(log2n) 有序 可重复 vector容器 + heap处理规则
set 红黑树 插入、删除、查找 O(log2n) 有序 不可重复
multiset 红黑树 插入、删除、查找 O(log2n) 有序 可重复
map 红黑树 插入、删除、查找 O(log2n) 有序 不可重复
multimap 红黑树 插入、删除、查找 O(log2n) 有序 可重复

STL中的swap函数

  • 除了数组,其他容器在交换后本质上是将内存地址进行了交换,而元素本身在内存中的位置是没有变化
  • swap在交换的时候并不是完全将2个容器的元素互换,而是交换了2个容器内的内存地址。

STL中的哈希表扩容

  • 这里需要知道STL中的swap底层,其实扩容也是vector扩容
    • 创建一个新桶,该桶是原来桶两倍大最接近的质数(判断n是不是质数的方法:用n除2到sqrt(n)范围内的数) ;
    • 将原来桶里的数通过指针的转换,插入到新桶中(注意STL这里做的很精细,没有直接将数据从旧桶遍历拷贝数据插入到新桶,而是通过指针转换两个桶的地址)
    • 通过swap函数将新桶和旧桶交换,销毁新桶

vector删除元素后如何避免当前迭代器会失效

删除时,将当前的迭代器存起来

STL迭代器失效的情况和原因

迭代器失效分三种情况考虑,也是分三种数据结构考虑,分别为数组型,链表型,树型数据结构。

  • 数组型数据结构
    • 该数据结构的元素是分配在连续的内存中
    • insert和erase操作,都会使得删除点和插入点之后的元素挪位置,所以,插入点和删除掉之后的迭代器全部失效,也就是说insert(*iter)(或erase(*iter)),然后在iter++,是没有意义的。
    • 解决方法:erase(*iter)的返回值是下一个有效迭代器的值。 iter = cont.erase(iter);
//不要直接在循环条件中写++iter
for (iter = cont.begin(); iter != cont.end();)
{
   (*it)->doSomething();
   if (shouldDelete(*iter))
      iter = cont.erase(iter);  //erase删除元素,返回下一个迭代器
   else
      ++iter;
}
  • 链表型数据结构

    • 对于list型的数据结构,使用了不连续分配的内存
    • 插入不会使得任何迭代器失效
    • 删除运算使指向删除位置的迭代器失效,但是不会失效其他迭代器.
    • 解决办法两种,erase(*iter)会返回下一个有效迭代器的值,或者erase(iter++).
  • 树形数据结构

    • 使用红黑树来存储数据
    • 插入不会使得任何迭代器失效
    • 删除运算使指向删除位置的迭代器失效,但是不会失效其他迭代器.
    • erase迭代器只是被删元素的迭代器失效,但是返回值为void,所以要采用erase(iter++)的方式删除迭代器。

注意 :经过erase(iter)之后的迭代器完全失效,该迭代器iter不能参与任何运算,包括iter++,*ite

vector的iterator和const_iterator和const iterator

  • 三种的区别
    • iterator,可遍历,可改变所指元素
    • const_iterator,可遍历,不可改变所指元素
    • const iterator,不可遍历,可改变所指元素
  • const_iterator转iterator,iterator不能转const_iterator
    • const_iterator 主要是 在容器被定义成常量、或者非常量容器但不想改变元素值的情况 下使用的,而且容器被定义成常量之后,它返回的迭代器只能是const_iterator
    • 有些容器成员函数只接受iterator作为参数,而不是const_iterator。那么,如果你只有一const_iterator,而你要在它所指向的容器位置上插入新元素呢?
      • const_iterator转iterator
      • 强制转换的函数会报错,只能通过 advance(a, distance(a, b)); 其中,distance用于取得两个迭代器之间的距离,advance用于将一个迭代器移动指定的距离
      • 如果a是iterator,b是const_iterator,distance会报错,可以显式的指明distance调用的模板参数类型,从而避免编译器自己得出它们的类型
typedef deque<int> IntDeque;
typedef IntDeque::iterator iter;
typedef IntDeque::const_iterator ConstIter;
IntDeque d;
ConstIter ci;
Iter i(d.begin());
advance(i,distance<ConstIter>(i,ci));