STL剖析

1. vector

是一个封装了动态大小数组的顺序容器;数组内容器严格按照线性顺序排序,支持随机访问,因此提供随机访问指针,例如vector::iterator ivite; 并且为了降低空间配置得速度成本,vector实际分配大小要比需求大一点。size()表示目前实际存储的大小,capacity()表示分配的空间大小,是要大于size的,start表示起始地址,finish表示最后一个数据地址,end_of_storage表示分配的最后地址。

扩容: 如果容量不足,容量会扩充至二倍,如果依旧不足,那就直接扩充到足够大的容量。
STL剖析_第1张图片

构造函数

  • vector():创建一个空vector
  • vector(int nSize):创建一个vector,元素个数为nSize
  • vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
  • vector(const vector&):复制构造函数
  • vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

增加元素:

  • void push_back(const T& x):向量尾部增加一个元素X
  • void emplace_back(const T&x):向量尾部增加一个元素X,与push_back不同,后者会先构造一个临时对象,然后将其复制移动到容器中,而emplace是直接在容器尾部调用构造函数构造对象,避免临时对象的创建和拷贝或移动。

删除函数:

  • iterator erase(iterator it):删除向量中迭代器指向元素
  • iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
  • void pop_back():删除向量中最后一个元素
  • void clear():清空向量中所有元素

遍历函数:

  • reference at(int pos):返回pos位置元素的引用
  • reference front():返回首元素的引用
  • reference back():返回尾元素的引用
  • iterator begin():返回向量头指针,指向第一个元素
  • iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置

其他函数:

  • bool empty() const:判断向量是否为空,若为空,则向量中无元素

  • int size() const:返回向量中元素的个数

  • void swap(vector&):交换两个同类型向量的数据

  • void insert():指定位置插入元素,多种重载版本

vector_insert_1vector_insert_2

对于insert函数,插入点后元素个数和要插入的元素个数不同,插入的算法也不相同,很神奇,研究了半天为什么这么做,其实主要就是考虑到finish后面的未初始化内存不能被copybackward函数操作,因此只能使用uninitialized_copy函数先处理这部分,先覆盖掉已经分配但未初始化的内存,但这个复制操作需要消耗性能,因此要尽可能少用。因此对于插入点后元素大于新增元素时,所以只能最后n个数据复制到finish位置后面去,虽然消耗性能,但没办法,剩下一点(前面的数据),就利用copybackward函数移动就好,最后再把新数据插入到指定位置;

但对于插入点后元素数量小于新增元素个数,说明要把最后的元素都移动到finish后面的未初始化内存中去,copybackward函数就办不到了,只能利用uninitialized_copy复制到最后去,在给指定位置填充新增元素,但要注意,uninitialized_fill_n函数性能比fill函数好,但前者只能处理未初始化内存,因此先用uninitialized_fill_n函数填充finsh后的多余未初始化内存区域(图中第一步),最后对于需要覆盖的内存(最后一步),再用fill,充分考虑性能问题。

2. list

其中 是一个非常重要的容器类,用于存储元素集合,支持双向迭代器。 是 C++ 标准模板库(STL)中的一个序列容器,它允许在容器的任意位置快速插入和删除元素。与数组或向量()不同, 不需要在创建时指定大小,并且可以在任何位置添加或删除元素,而不需要重新分配内存。可以理解为一个双向链表,分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中。list基于双向链表的结构, 他所以可以在序列已知的任何位置快速插入或删除元素(时间复杂度为O(1))。并且在 list 容器中移动元素,也比其它容器的效率高。但是不支持随机访问。因此list提供的是双向迭代器。

相比于vector,list迭代器不会因为insert而失效,只会在erase操作时,对应的一个迭代器会失效。而vector存在严重的迭代器失效场景

迭代器:

  • begin() 返回指向容器中第一个元素的双向迭代器。

  • end() 返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器。

  • rbegin() 返回指向最后一个元素的反向双向迭代器。

  • rend() 返回指向第一个元素所在位置前一个位置的反向双向迭代器。

  • cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

  • cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

  • crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

  • crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

  • empty() 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。

  • size() 返回当前容器实际包含的元素个数。

  • max_size() 返回容器所能包含元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。

访问:

  • front() 返回第一个元素的引用。
  • back() 返回最后一个元素的引用。
  • assign() 用新元素替换容器中原有内容。
  • emplace_front() 在容器头部生成一个元素。该函数和 push_front() 的功能相同,但效率更高。
  • push_front() 在容器头部插入一个元素。
  • pop_front() 删除容器头部的一个元素。
  • emplace_back() 在容器尾部直接生成一个元素。该函数和 push_back() 的功能相同,但效率更高。
  • push_back() 在容器尾部插入一个元素。
  • pop_back() 删除容器尾部的一个元素。
  • emplace() 在容器中的指定位置插入元素。该函数和 insert() 功能相同,但效率更高。
  • insert() 在容器中的指定位置插入元素。
  • erase() 删除容器中一个或某区域内的元素。
  • swap() 交换两个容器中的元素,必须保证这两个容器中存储的元素类型是相同的。
  • resize() 调整容器的大小。
  • clear() 删除容器存储的所有元素。
  • splice() 将一个 list 容器中的元素插入到另一个容器的指定位置。
  • remove(val) 删除容器中所有等于 val 的元素。
  • remove_if() 删除容器中满足条件的元素。
  • unique() 删除容器中相邻的重复元素,只保留一个。
  • merge() 合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。
  • sort() 通过更改容器中元素的位置,将它们进行排序。
  • reverse() 反转容器中元素的顺序

3.deque

是标准模板库(STL)的一部分,它提供了双端队列(double-ended queue)的实现。双端队列是一种允许在两端进行插入和删除操作的线性数据结构。它提供了快速的随机访问能力,同时允许在两端进行高效的插入和删除操作。这使得 成为处理需要频繁插入和删除元素的场景的理想选择。

与vector区别:vector虽然技术上也提供了两端数据的插入和删除,但本质是单向的连续线性空间,头部操作需要移动所有元素,效率太低‘deque允许常数时间内对端头元素增删;deque没有容量,是动态的以分段连续空间组合而成。随时可以增加一段新的空间链接起来。

虽然提供随即迭代器,但效率远不如vector,因此数组操作尽可能使用vector。并且在deque中进行sort,会先把数据拷贝在vector中,进行sort,然后在放回deque中,效率底下,因此deque只适合于在两端增删的场景——stack和queue

  • deque() 默认构造函数,创建一个空的 deque 容器。
    deque(size_type n) 创建一个包含 n 个默认值元素的 deque 容器。
    deque(size_type n, const T& value) 创建一个包含 n 个值为 valuedeque 容器。
    deque(initializer_list il) 使用初始化列表 il 构造 deque 容器。
    operator= 赋值操作符,赋值给 deque 容器。
    assign() 用新值替换 deque 容器中的所有元素。
    at(size_type pos) 返回 pos 位置的元素,并进行范围检查。
    operator[](size_type pos) 返回 pos 位置的元素,不进行范围检查。
    front() 返回第一个元素的引用。
    back() 返回最后一个元素的引用。
    begin() 返回指向第一个元素的迭代器。
    end() 返回指向末尾元素后一位置的迭代器。
    rbegin() 返回指向最后一个元素的逆向迭代器。
    rend() 返回指向第一个元素之前位置的逆向迭代器。
    empty() 检查容器是否为空。
    size() 返回容器中的元素个数。
    max_size() 返回容器可容纳的最大元素个数。
    clear() 清除容器中的所有元素。
    insert(iterator pos, const T& value) pos 位置插入 value 元素。
    erase(iterator pos) 移除 pos 位置的元素。
    push_back(const T& value) 在容器末尾添加 value 元素。
    pop_back() 移除容器末尾的元素。
    push_front(const T& value) 在容器前端添加 value 元素。
    pop_front() 移除容器前端的元素。
    resize(size_type count) 调整容器大小为 count,多出部分用默认值填充。
    swap(deque& other) 交换两个 deque 容器的内容。
    get_allocator() 返回一个用于构造双端队列的分配器对象的副本。

stack: 栈,先进后出,一端进出,不允许遍历,这种改变容器接口,得到另一种结构的称为配接器

==queue:==队列,先进先出,一端只进,另一端只出,

4.set

关联容器,它存储了一组唯一的元素,并按照一定的顺序进行排序。 提供了高效的元素查找、插入和删除操作。它是基于红黑树实现的,因此具有对数时间复杂度的查找、插入和删除性能。有序、不能重复。同时迭代器失效只存在于被删除元素。

  • insert(元素): 插入一个元素。
  • erase(元素): 删除一个元素。
  • find(元素): 查找一个元素。
  • size(): 返回容器中元素的数量。
  • empty(): 检查容器是否为空。

5.unordered_set

提供了一种基于哈希表的容器,用于存储唯一的元素集合。与 set 不同,unordered_set 不保证元素的排序,但通常提供更快的查找、插入和删除操作。

无序、不能重复、有哈希函数

  • insert(元素): 插入一个元素。
  • erase(元素): 删除一个元素。
  • find(元素): 查找一个元素。
  • size(): 返回容器中元素的数量。
  • empty(): 检查容器是否为空。

6. map

是标准模板库(STL)的一部分,它提供了一种关联容器,用于存储键值对(key-value pairs)。map 容器中的元素是按照键的顺序自动排序的,这使得它非常适合需要快速查找和有序数据的场景。有序、键值对、唯一、双向迭代。底层红黑树

7.unordered_map

提供了一种基于哈希表的键值对容器。与 std::map 不同,unordered_map 不保证元素的排序,但通常提供更快的查找速度。

无序、键值对、唯一

8.priority_queue

优先队列,缺省由高到低,由maxheap实现,底层是一个vector的完全二叉树。同样是一种适配器;

push:先利用push_back放入底层vector中,在利用push_heap重新排序,实现优先队列的插入

pop:先利用pop_heap,把最大(最小)元素放到vector尾部,再利用pop_back弹出。

优先队列只有顶部top元素能被访问,因此不提供迭代器

9. slist(旧版,目前C++标准库不支持)

单向链表,对应迭代器是单向的,虽然功能不如list,但slist内存小,某些操作更快,特定需求下比list性能好。

单向链表特点决定,某个节点插入数据,只能插入该节点之后,这与STL其他容器操作相反(例如list,某个点插入新数据,新数据是在原始节点之前的,vector也是),因此提供insert_after,erase_after。同时,链表尾部(back)增删数据显然效率低下,因此slist只提供push_front,pop_front。也就是虚拟头节点后增删数据,因此如果给一个slist插入数据1,2,3,4。实际单链表数据是相反的4,3,2,1有没有栈的味道?
其他容器操作相反(例如list,某个点插入新数据,新数据是在原始节点之前的,vector也是),因此提供insert_after,erase_after。同时,链表尾部(back)增删数据显然效率低下,因此slist只提供push_front,pop_front。也就是虚拟头节点后增删数据,因此如果给一个slist插入数据1,2,3,4。实际单链表数据是相反的4,3,2,1有没有栈的味道?

你可能感兴趣的:(C++,Primer,Plus笔记,数据结构,c++,开发语言)