首页 > 编程语言 >STL容器和算法

STL容器和算法

时间:2024-10-17 22:53:05浏览次数:3  
标签:容器 迭代 deque STL 元素 算法 vector 空间 节点

1、C++的STL介绍(内存管理、allocator、函数、实现机理、多线程实现)

STL一共提供六大组件,包括容器、算法、迭代器、仿函数、配接器和配置器,彼此可以组合套用。容器通过配置器取得数据存储空间,算法通过迭代器存取容器内容,仿函数可以协助算法完成不同的策略变化,配接器可以应用于容器、仿函数和迭代器。

容器:各种数据结构,如vector、list、deque、set、map,用来存放数据,从实现的角度来讲是一种类模板。

算法:各种常用的算法,如sort(插入、快排、堆排序),search(二分查找),从实现的角度来讲是一种方法模板。

迭代器:从实现的角度来看,迭代器是一种将operator*,operator->,operator++,operator--等指针相关操作赋予重载的类模板,所有的STL容器都有自己的迭代器。

仿函数:从实现的角度来看,仿函数是一种重载了operator()的类或者类模板。可以帮助算法实现不同的策略。

配接器:一种用来修饰容器或者仿函数或迭代器接口的东西。

配置器:负责空间配置与管理,从实现的角度讲,配置器是一个实现了动态空间配置、空间管理、空间释放的类模板。

拓展:内存管理allocator

SGI设计了双层级配置器,第一级配置器直接使用malloc()和free()完成内存的分配和回收。第二级配置器则根据需求量的大小选择不同的策略执行。

对于第二级配置器,如果需求块大小大于128bytes,则直接转而调用第一级配置器,使用malloc()分配内存。如果需求块大小小于128bytes,第二级配置器中维护了16个自由链表,负责16种小型区块的次配置能力。

即当有小于128bytes的需求块要求时,首先查看所需需求块大小所对应的链表中是否有空闲空间,如果有直接返回,如果没有,则向内存池中申请所需需求块大小的内存空间,如果申请成功,则将其加入到自由链表中。如果内存池中没有空间,则使用malloc()从堆中进行申请,且申请到的大小是需求量的二倍(或二倍+n附加量),一倍放在自由空间中,一倍(或一倍+n)放入内存池中。

如果malloc()也失败,则会遍历自由空间链表,四处寻找“尚有未用区块,且区块够大”的freelist,找到一块就挖出一块交出。如果还是没有,仍交由malloc()处理,因为malloc()有out-of-memory处理机制或许有机会释放其他的内存拿来用,如果可以就成功,如果不行就报bad_alloc异常。

STL中序列式容器的实现

vector:是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。vector维护的是一个连续的线性空间,而且普通指针就可以满足要求作为vector的迭代器(RandomAccessIterator)。vector的数据结构中其实就是三个迭代器构成的,一个指向目前使用空间头的iterator,一个指向目前使用空间尾的iterator,一个指向目前可用空间尾的iterator。当有新的元素插入时,如果目前容量够用则直接插入,如果容量不够,则容量扩充至两倍,如果两倍容量不足,就扩张至足够大的容量。

扩充的过程并不是直接在原有空间后面追加容量,二十重新申请一块连续空间,将原有的数据拷贝到新空间中,再释放原有空间,完成一次扩充。需要注意的是,每次扩充是重新开辟的空间,所以扩充后,原有的迭代器会失效。

List:与vector相比,list的好处就是每次插入或删除一个元素,就配置或释放一个空间,而且原有的迭代器也不会失效。STL list是一个双向链表,普通指针已经不能满足list迭代器的需求,因为list的存储空间是不连续的。list的迭代器必需具备前移和后退功能,所以list提供的是Bidirectionallterator。list的数据结构中只要一个指向node节点的指针就可以了。

deque:vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间。所谓双向开口,就是说deque支持从头尾两端进行元素的插入和删除操作。相比于vector的扩充空间的方式,deque实际上更加贴切的实现了动态空间的概念。deque没有容量的概念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并连接起来。

由于要维护这种整体连续的假象,并提供随机存取的接口(即也提供RandomAccessIterator),避开了“重新配置,复制,释放”的轮回,代价是复杂的迭代器结构。也就是说除非必要,我们应该尽可能的使用vector,而不是deque。

那么我们回过来具体说deque是如何做到维护整体连续的假象的,deque采用一块所谓的map作为主控,这里的map实际上就是一块大小连续的空间,其中每一个元素,我们称为节点node,都指向了另一端连续线性空间称为缓冲区,缓冲区才是deque的真正存储空间主体。

SGI STL是允许我们指定缓冲区的大小的,默认0表示使用512bytes缓冲区。当map满载时,我们选用一块更大的空间来作为map,重新调整配置。deque另外一个关键的就是它的iterator的设计,deque的iterator中四个部分,cur指向缓冲区现行元素,first指向缓冲区的头,last指向缓冲区的尾(有时会包含备用空间),node指向管控中心。所以总结来说,deque的数据结构中包含了指向第一个节点的iterator start,和指向最后一个节点的iterator finish,一块连续空间作为主控map,也需要记住map的大小,以备判断何时配置更大的map。

stack:是一种先进后出的数据结构,只有一个出口,stack允许从最顶端新增元素,移除顶端元素,取得最顶端元素。deque是双向开口的数据结构,所以使用deque作为底部结构并封存器头端开口,就形成了一个stack。

queue:是一种先进先出的数据结构,有两个出口,允许从最低端加入元素,取得最顶端元素,从最底端新增元素。就形成了一个queue。(其实list也可以实现deque)

heap:堆并不属于STL容器组件,它是个幕后英雄,扮演priority_queue的助手,priority_queue允许用户以任何次序将任何元素推入容器内,但取出时一定是从优先权最高(数值最高)的元素开始取。大根堆(binary max heap)正具有这样的性质,适合作为priority_queue的底层机制。

大根堆是一个满足每个节点的键值都大于或等于其子节点键值的二叉树(具体实现是一个vector,一块连续空间,通过维护某种顺序来实现这个二叉树),新加入元素时,新加入的元素要放在最下一层为叶节点,即具体实现是填补在由左至右的第一个空格(即把新元素插入在底层vector的end()),然后执行一个所谓上溯的程序:将新节点拿来与父节点比较,如果其键值比父节点大,就父子对换位置,如此一直上溯,知道不需要对换或直到根节点为止。当取出一个元素时,最大值在根节点,取走根节点,要割舍最下层最右边的右节点,并将其值重新安插至最大堆,最末节点放入根节点,进行一个下溯程序:将空间节点和其较大的节点对调,并持续下方,知道叶节点为止。

priority_queue:底层时一个vector,使用heap形成的算法插入,获取heap中元素的算法,维护这个vector,以达到允许用户以任何次序将任何元素插入容器内,但取出时一定是从优先权最高(数值最高)的元素开始区的目的。

slist:STL list是一个双向链表,slist是一个单向链表。

标签:容器,迭代,deque,STL,元素,算法,vector,空间,节点
From: https://blog.csdn.net/weixin_68927712/article/details/142991285

相关文章

  • 武汉大学卫星导航算法程序设计——解码与数据获取
    还在为解码发愁吗?面对二进制文件还是无从下手吗?一篇文章帮你搞定。我们从接收机获取的数据并不是rinex格式的文件,而是NovAtel数据格式的二进制文件。我们需要从文件中提取出我们需要的导航数据,也就是解码的过程。废话不多说,我们直接开始讲解。一、Binary数据头格式请不要使......
  • 排序算法 - 快速排序
    排序算法-快速排序  概要  快速排序(Quicksort)是对冒泡排序算法的一种改进。快速排序是一种基于分而治之的排序算法。  它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都要小。然后,再按......
  • 【算法】C++中的二分查找
    ......
  • 代码随想录算法训练营 | 1143.最长公共子序列,1035.不相交的线,53. 最大子序和,392.判断
    1143.最长公共子序列题目链接:1143.最长公共子序列文档讲解︰代码随想录(programmercarl.com)视频讲解︰最长公共子序列日期:2024-10-17想法:这里的子序列不要求连续了,dp[i][j]要表示为在text1[0,i-1]和text2[0,j-1]的范围里,最长的公共子序列长度,-1同样是为了初始化方便,如果te......
  • 吴恩达深度学习笔记(4)---加速神经网络训练速度的优化算法
    机器学习的应用是一个高度依赖经验,不断重复的过程,需要训练很多模型才能找到一个确实好用的。小批量梯度下降算法:矢量化可以有效计算m个算例而不需要for循环,因此我们需要将所有的训练样例放入巨型矩阵中。但是当数据量超大时,计算时间仍需很久,可以考虑将训练集分为微小的训练集......
  • (算法)最⻓湍流⼦数组————<动态规划>
    1.题⽬链接:978.最⻓湍流⼦数组2.题⽬描述:3.解法(动态规划):算法思路:1.状态表⽰:我们先尝试定义状态表⽰为:dp[i]表⽰「以i位置为结尾的最⻓湍流数组的⻓度」。 但是,问题来了,如果状态表⽰这样定义的话,以i位置为结尾的最⻓湍流数组的⻓度我们没法从之前的状态推导出......
  • (算法)等差数列划分————<动态规划>
    1.题⽬链接:413.等差数列划分2.题⽬描述:3.解法(动态规划):算法思路:1.状态表⽰:由于我们的研究对象是「⼀段连续的区间」,如果我们状态表⽰定义成[0,i]区间内⼀共有多少等差数列,那么我们在分析dp[i]的状态转移时,会⽆从下⼿,因为我们不清楚前⾯那么多的「等差数列都在什......
  • 图论day64 :最短路径算法 | SPFA(Bellman_ford的队列优化版)、城市间货物运输 I、Ⅱ、Ⅲ
    图论day64:最短路径算法|SPFA(Bellman_ford的队列优化版)、94.城市间货物运输I(卡码网)【SPFA算法+邻接表优化】、95.城市间货物运输II(判断负权回路)、96.城市间货物运输III【已知有负权回路,该如何计算】、Bellman_ford算法思维导图汇总SPFA(Bellman_ford的队列优化版)94......
  • 天幕容器vector的底层实现,让这个容器的建造在你面前一览无余
    文章目录一、什么是vector?二、bit::vector的设计思路三、bit::vector的类定义四、构造函数与析构函数1.默认构造函数2.区间构造函数3.填充构造函数4.初始化列表构造函数5.拷贝构造函数6.析构函数五、push_back和内存管理六、插入操作(insert)七、删除操作(e......
  • 从单细胞和空间转录组学推断模式驱动的细胞间流动(Flowsig)--生信算法笔记
    **Inferringpattern-drivingintercellularflowsfromsingle-cellandspatialtranscriptomics**Almet,A.A.,Tsai,YC.,Watanabe,M.etal.Inferringpattern-drivingintercellularflowsfromsingle-cellandspatialtranscriptomics.NatMethods21,1806......