STL介绍以及其底层实现?包括vector, map, list

1 介绍一下STL?

STL,Standard Template Library,即标准模板库。包括如下六个组件:

它们之间的关系是:分配器给容器分配存储空间,算法通过迭代器获取容器中的内容,仿函数可以协助算法完成各种操作,配接器用来套接适配仿函数。

1) 容器

容器就是我们常见的各种数据结构,例如关联式容器:map, set, multimap,非关联式容器:vector, list, deque。这些数据结构主要是用于存放数据,STL是一种class temple。

  • array 是固定大小的顺序容器,它们保存了一个以严格的线性顺序排列的特定数量的元素。
  • vector
    • 是表示可以改变大小的数组的序列容器
    • 底层数据结构为数组,支持快速随机访问
  • deque(双端队列)是具有动态大小的序列容器,可以在两端扩展或收缩
  • forward_list(单向链表)是序列容器,允许在序列中的任何地方进行恒定的时间插入和擦除操作。
  • list
    • 是序列容器,允许在序列中的任何地方进行常数时间插入和擦除操作,并在两个方向上进行迭代
    • 底层数据结构为双向链表,支持快速增删,不支持随机访问
    • 无序、可重复
  • stack 是一种容器适配器,用于在LIFO(后进先出)的操作,其中元素仅从容器的一端插入和提取。
  • queue 是一种容器适配器,用于在FIFO(先入先出)的操作,其中元素插入到容器的一端并从另一端提取。
  • map
    • 是关联容器,按照特定顺序存储由 key value (键值) 和 mapped value (映射值) 组合形成的元素。
    • 底层数据结构为红黑树
    • 有序,不可重复
    • 查找的时间复杂度为O(logn)

2)算法

STL常见的算法有sort, search, copy, erase, for_search, unique,从实现角度来看,STL算法是一种function template。

3)迭代器

是作为容器和算法之间的一种胶合剂,是所谓的“泛型指针”,共有5种类型,以及其他衍生变化。从实现角度来看,迭代器是一种将operate*,operate->,operate++,operate–等指针相关操作予以重载的class template。

4)仿函数

行为类似函数,可作为算法的某种策略。从实现角度来看,仿函数是一种重载了operate()的class,或class template,一般函数指针可视为狭义的仿函数。

5)配接器

一种用来修饰容器,仿函数,迭代器接口的东西,STL提供的queue和stack,虽然看似容器,其实只能算是一种容器适配器,因为他们的底层全部借由其他容器供应。改变functor接口者,称之为function adapter;改变container接口者,称之为container adapter,改变iterator接口者,称之为container adapter。

6)配置器

主要负责空间配置与管理,从实现角度来看,配置器是一种实现了动态空间配置,空间管理,空间释放的class template。

2 STL如何实现vector、list、map的底层机制?

1)vector

  • 使用动态数组的方式实现的,里面有一个指针指向一片连续的内存空间。
  • 如果动态内存的数组空间不够,就要动态地重新分配内存,一般是当前大小的两倍,然后把原数组的内容拷贝过去,接着释放原来的空间。
  • 所以在一般情况下,其访问速度同一般数组,只有在重新分配内存发生时,其性能才会下降。
  • vector的size()表示数组中元素个数有多少,capacity()表示数组有多大容量。

为什么vector的插入操作可能导致迭代器失效?

因为vector动态增加大小时,并不是在原空间后增加新的空间,而是以原大小的两倍在另外配置一片较大的新空间,然后将内容拷贝过来,并释放原来的空间。由于操作改变了空间,所以迭代器失效。

2)list

  • 用双向链表来实现的,是一种物理存储单元上非连续、非顺序的存储结构。
  • 以结点为单位存放数据,结点的地址在内存中不一定连续,每次插入或删除一个元素,就配置或释放一个元素空间。

3)map

  • map以红黑树为底层机制。红黑树是一种二叉平衡搜索树,自动排序效果不错。

3 vector和list的区别

  1. vector底层实现是数组;list是双向链表。
  2. vector支持随机访问,list不支持。
  3. vector是顺序存储,list不是。
  4. vector一次性分配好内存,不够时才进行2倍扩容;list每次插入新节点都会进行内存申请。
  5. vector随机访问性能好,插入删除性能差;list随机访问性能差,插入删除性能好。

vector插入删除和list有什么区别?

  • vector插入和删除数据,需要对现有数据进行复制移动。(因为其底层实现是数组,存放的数据是连续的内存地址)如果vector存储的对象很大,或者析构函数很复杂,则开销较大。如果是简单的小数据,效率优于list。
    • 时间复杂度为O(n)
  • list插入和删除数据,需要对现有数据进行遍历(因为其底层实现是链表),但在首部插入数据,效率很高。
    • 时间复杂度为O(n)